blob: 7144ad7ccdfe801d1a08fba6372befe0377c7471 [file] [log] [blame]
<template>
<div>
<div class="post-inner">
<div v-if="toc.length" class="table-of-contents">
<h4 class="toc-container-header">{{ $t('inThisPage') }}</h4>
<ul>
<li
v-for="link of toc"
:key="link.id"
:class="{ toc2: link.depth === 2, toc3: link.depth === 3 }"
>
<NuxtLink :to="`#${link.id}`">{{ link.title }}</NuxtLink>
</li>
</ul>
</div>
<!--
'nuxt-content' class for DocSearch
https://github.com/algolia/docsearch-configs/blob/master/configs/apache_echarts.json
-->
<div class="nuxt-content">
<post-content :content="html"></post-content>
</div>
</div>
<contributors :path="postPath"></contributors>
</div>
</template>
<script lang="ts">
import '~/components/markdown/global'
import markdown from 'markdown-it'
import anchor from 'markdown-it-anchor'
import Contributors from '~/components/partials/Contributors.vue'
import PostContent from '~/components/partials/PostContent'
import * as base64 from 'js-base64'
import config from '~/configs/config'
import LazyLoad from 'vanilla-lazyload'
function parseLiveCodeBlocks(md: string) {
return md.replace(
/^```(\w+?)\s+live\s*({.*?})?\s*?\n([\s\S]+?)^```/gm,
(full, lang = 'js', options = '{}', code: string = '') => {
lang = lang.trim()
options = options.trim() || '{}'
const encoded = base64.encode(code.trim(), true)
return `<md-live lang="${lang}" code="'${encoded}'" v-bind="${options}" />`
}
)
}
function parseCodeBlocks(md: string) {
return md.replace(
/^```(\w+?)\s*({.*?})?\s*?\n([\s\S]+?)^```/gm,
(full, lang = 'js', lineHighlights = '', code: string = '') => {
lang = lang.trim()
const encoded = base64.encode(code.trim(), true)
return `<md-code-block lang="${lang}" code="'${encoded}'" line-highlights="'${lineHighlights}'" />`
}
)
}
function replaceVars(md: string, lang: string) {
// Replace variables
;[
'optionPath',
'mainSitePath',
'exampleViewPath',
'exampleEditorPath'
].forEach(p => {
const val = config[p].replace('${lang}', lang)
md = md.replace(new RegExp('\\$\\{' + p + '\\}', 'g'), val)
})
md = md.replace(/\$\{lang\}/g, lang)
return md
}
function slugify(s: string) {
return encodeURIComponent(
String(s)
.trim()
.toLowerCase()
.replace(/\s+/g, '-')
)
}
export default {
components: {
Contributors,
PostContent
},
data() {
return {
toc: [] as {
title: string
id: string
depth: number
}[]
}
},
mounted() {
// @ts-ignore
this.toc = []
const headers =
// @ts-ignore
this.$el.querySelector('.post-inner')?.querySelectorAll('h2,h3') || []
for (let i = 0; i < headers.length; i++) {
const title = (headers[i] as HTMLHeadingElement).innerText
// @ts-ignore
this.toc.push({
title,
depth: +headers[i].nodeName.replace(/\D/g, ''),
id: slugify(title)
})
}
setTimeout(() => {
// FIXME not sure why this needs to be in the setTimeout
// init lazy load
// @ts-ignore
this._lazyload = new LazyLoad({
// container: this.$el.querySelector('.post-inner') as HTMLElement,
elements_selector: 'img[data-src], iframe[data-src]',
threshold: 300
})
})
},
destroyed() {
// @ts-ignore
this._lazyload && this._lazyload.destroy()
},
head() {
return {
meta: [
{
hid: 'docsearch:language',
name: 'docsearch:language',
// @ts-ignore
content: this.$i18n.locale
}
]
}
},
async asyncData({ params, i18n }: any) {
const postPath = `${i18n.locale}/${params.pathMatch}`
const fileContent = await import(`~/contents/${postPath}.md`)
const content = replaceVars(
parseCodeBlocks(parseLiveCodeBlocks(fileContent.default)),
i18n.locale
)
const md = markdown({
html: true,
linkify: true
})
.use(anchor, {
// slugify,
permalink: false,
permalinkAfter: true,
permalinkSymbol: '#',
permalinkClass: 'permalink'
})
.use(function(md) {
const defaultImageRenderer = md.renderer.rules.image
md.renderer.rules.image = function(tokens, idx, options, env, self) {
const token = tokens[idx]
const srcValue = token.attrGet('src')
token.attrPush(['data-src', srcValue])
token.attrSet('src', '')
return defaultImageRenderer(tokens, idx, options, env, self)
}
}) // lazyload
return { html: md.render(content), postPath }
}
}
</script>
<style lang="scss"></style>