| <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> |