refactor(core): and markdown compiler

This commit is contained in:
qingwei.li 2017-02-18 14:09:17 +08:00 committed by cinwell.li
commit fe88c154b0
12 changed files with 194 additions and 232 deletions

View file

@ -1,26 +1,56 @@
import marked from 'marked'
import Prism from 'prismjs'
import { helper as helperTpl } from './tpl'
import { slugify, clearSlugCache } from './slugify'
import { emojify } from './emojify'
import { toURL } from '../route/hash'
import { isFn, merge, cached } from '../util/core'
export const renderer = new marked.Renderer()
export function markdown () {
}
let markdownCompiler = marked
let contentBase = ''
let renderer = new marked.Renderer()
const toc = []
/**
* Compile markdown content
*/
export const markdown = cached(text => {
let html = ''
if (!text) return text
html = markdownCompiler(text)
html = emojify(html)
clearSlugCache()
return html
})
markdown.renderer = renderer
markdown.init = function (config = {}, context = window.location.pathname) {
contentBase = context
if (isFn(config)) {
markdownCompiler = config(marked, renderer)
} else {
renderer = merge(renderer, config.renderer)
marked.setOptions(merge(config, { renderer }))
}
}
/**
* render anchor tag
* @link https://github.com/chjj/marked#overriding-renderer-methods
*/
renderer.heading = function (text, level) {
const slug = slugify(text)
let route = ''
const url = toURL(contentBase, { id: slug })
route = `#/${getRoute()}`
toc.push({ level, slug: `${route}#${encodeURIComponent(slug)}`, title: text })
toc.push({ level, slug: url, title: text })
return `<h${level} id="${slug}"><a href="${route}#${slug}" data-id="${slug}" class="anchor"><span>${text}</span></a></h${level}>`
return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${text}</span></a></h${level}>`
}
// highlight code
renderer.code = function (code, lang = '') {
@ -30,21 +60,31 @@ renderer.code = function (code, lang = '') {
}
renderer.link = function (href, title, text) {
if (!/:|(\/{2})/.test(href)) {
// TODO
href = `#/${href}`.replace(/\/+/g, '/')
}
return `<a href="${href}" title="${title || ''}">${text}</a>`
}
renderer.paragraph = function (text) {
if (/^!&gt;/.test(text)) {
return tpl.helper('tip', text)
return helperTpl('tip', text)
} else if (/^\?&gt;/.test(text)) {
return tpl.helper('warn', text)
return helperTpl('warn', text)
}
return `<p>${text}</p>`
}
renderer.image = function (href, title, text) {
const url = /:|(\/{2})/.test(href) ? href : ($docsify.basePath + href).replace(/\/+/g, '/')
const titleHTML = title ? ` title="${title}"` : ''
// TODO
// get base path
// const url = /:|(\/{2})/.test(href) ? href : ($docsify.basePath + href).replace(/\/+/g, '/')
// const titleHTML = title ? ` title="${title}"` : ''
// return `<img src="${url}" alt="${text}"${titleHTML} />`
}
/**
* Compile sidebar
*/
export function sidebar (text) {
return `<img src="${url}" alt="${text}"${titleHTML} />`
}

View file

@ -0,0 +1,6 @@
export function emojify (text) {
return text
.replace(/<(pre|template)[^>]*?>([\s\S]+)<\/(pre|template)>/g, m => m.replace(/:/g, '__colon__'))
.replace(/:(\w+?):/ig, '<img class="emoji" src="https://assets-cdn.github.com/images/icons/emoji/$1.png" alt="$1" />')
.replace(/__colon__/g, ':')
}

View file

@ -0,0 +1,27 @@
/**
* gen toc tree
* @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81
* @param {Array} toc
* @param {Number} maxLevel
* @return {Array}
*/
export function genTree (toc, maxLevel) {
const headlines = []
const last = {}
toc.forEach(headline => {
const level = headline.level || 1
const len = level - 1
if (level > maxLevel) return
if (last[len]) {
last[len].children = last[len].children || []
last[len].children.push(headline)
} else {
headlines.push(headline)
}
last[level] = headline
})
return headlines
}

View file

@ -1,30 +1,47 @@
import * as dom from '../util/dom'
import cssVars from '../util/polyfill/css-vars'
import * as tpl from './tpl'
import { markdown, sidebar } from './compiler'
import { callHook } from '../init/lifecycle'
function renderMain () {
}
function renderNav () {
}
function renderSidebar () {
function renderMain (html) {
if (!html) {
// TODO: Custom 404 page
}
this._renderTo('.markdown-section', html)
}
export function renderMixin (Docsify) {
Docsify.prototype._renderTo = function (el, content, replace) {
const proto = Docsify.prototype
proto._renderTo = function (el, content, replace) {
const node = dom.getNode(el)
if (node) node[replace ? 'outerHTML' : 'innerHTML'] = content
}
Docsify.prototype._renderSidebar = renderSidebar
Docsify.prototype._renderNav = renderNav
Docsify.prototype._renderMain = renderMain
proto._renderSidebar = function (text) {
this._renderTo('.sidebar-nav', sidebar(text))
// bind event
}
proto._renderNav = function (text) {
this._renderTo('nav', markdown(text))
}
proto._renderMain = function (text) {
callHook(this, 'beforeEach', text, result => {
const html = markdown(result)
callHook(this, 'afterEach', html, text => renderMain.call(this, text))
})
}
}
export function initRender (vm) {
const config = vm.config
// Init markdown compiler
markdown.init(vm.config.markdown)
const id = config.el || '#app'
const navEl = dom.find('nav') || dom.create('nav')

View file

@ -0,0 +1,27 @@
let cache = {}
const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,.\/:;<=>?@\[\]^`{|}~]/g
export function slugify (str) {
if (typeof str !== 'string') return ''
let slug = str.toLowerCase().trim()
.replace(/<[^>\d]+>/g, '')
.replace(re, '')
.replace(/\s/g, '-')
.replace(/-+/g, '-')
.replace(/^(\d)/, '_$1')
let count = cache[slug]
count = cache.hasOwnProperty(slug) ? (count + 1) : 0
cache[slug] = count
if (count) {
slug = slug + '-' + count
}
return slug
}
export function clearSlugCache () {
cache = {}
}