UNPKG

docsify

Version:

A magical documentation generator.

223 lines (184 loc) 5.91 kB
import * as dom from '../util/dom' import * as tpl from './tpl' import cssVars from '../util/polyfill/css-vars' import tinydate from 'tinydate' import { callHook } from '../init/lifecycle' import { Compiler } from './compiler' import { getAndActive, sticky } from '../event/sidebar' import { getPath, isAbsolutePath } from '../router/util' import { isMobile } from '../util/env' import { isPrimitive } from '../util/core' import { scrollActiveSidebar, scroll2Top } from '../event/scroll' function executeScript () { const script = dom.findAll('.markdown-section>script') .filter(s => !/template/.test(s.type))[0] if (!script) return false const code = script.innerText.trim() if (!code) return false setTimeout(_ => { window.__EXECUTE_RESULT__ = new Function(code)() }, 0) } function formatUpdated (html, updated, fn) { updated = typeof fn === 'function' ? fn(updated) : typeof fn === 'string' ? tinydate(fn)(new Date(updated)) : updated return html.replace(/{docsify-updated}/g, updated) } function renderMain (html) { if (!html) { // TODO: Custom 404 page html = 'not found' } this._renderTo('.markdown-section', html) // Render sidebar with the TOC !this.config.loadSidebar && this._renderSidebar() // execute script if (this.config.executeScript !== false && typeof window.Vue !== 'undefined' && !executeScript()) { setTimeout(_ => { const vueVM = window.__EXECUTE_RESULT__ vueVM && vueVM.$destroy && vueVM.$destroy() window.__EXECUTE_RESULT__ = new window.Vue().$mount('#main') }, 0) } else { this.config.executeScript && executeScript() } } function renderNameLink (vm) { const el = dom.getNode('.app-name-link') const nameLink = vm.config.nameLink const path = vm.route.path if (!el) return if (isPrimitive(vm.config.nameLink)) { el.setAttribute('href', nameLink) } else if (typeof nameLink === 'object') { const match = Object.keys(nameLink).filter(key => path.indexOf(key) > -1)[0] el.setAttribute('href', nameLink[match]) } } export function renderMixin (proto) { proto._renderTo = function (el, content, replace) { const node = dom.getNode(el) if (node) node[replace ? 'outerHTML' : 'innerHTML'] = content } proto._renderSidebar = function (text) { const { maxLevel, subMaxLevel, loadSidebar } = this.config this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel)) const activeEl = getAndActive(this.router, '.sidebar-nav', true, true) if (loadSidebar && activeEl) { activeEl.parentNode.innerHTML += (this.compiler.subSidebar(subMaxLevel) || '') } else { // reset toc this.compiler.subSidebar() } // bind event this._bindEventOnRendered(activeEl) } proto._bindEventOnRendered = function (activeEl) { const { autoHeader, auto2top } = this.config scrollActiveSidebar(this.router) if (autoHeader && activeEl) { const main = dom.getNode('#main') const firstNode = main.children[0] if (firstNode && firstNode.tagName !== 'H1') { const h1 = dom.create('h1') h1.innerText = activeEl.innerText dom.before(main, h1) } } auto2top && scroll2Top(auto2top) } proto._renderNav = function (text) { text && this._renderTo('nav', this.compiler.compile(text)) getAndActive(this.router, 'nav') } proto._renderMain = function (text, opt = {}) { if (!text) { return renderMain.call(this, text) } callHook(this, 'beforeEach', text, result => { let html = this.isHTML ? result : this.compiler.compile(result) if (opt.updatedAt) { html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated) } callHook(this, 'afterEach', html, text => renderMain.call(this, text)) }) } proto._renderCover = function (text) { const el = dom.getNode('.cover') if (!text) { dom.toggleClass(el, 'remove', 'show') return } dom.toggleClass(el, 'add', 'show') let html = this.coverIsHTML ? text : this.compiler.cover(text) const m = html.trim().match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$') if (m) { if (m[2] === 'color') { el.style.background = m[1] + (m[3] || '') } else { let path = m[1] dom.toggleClass(el, 'add', 'has-mask') if (!isAbsolutePath(m[1])) { path = getPath(this.router.getBasePath(), m[1]) } el.style.backgroundImage = `url(${path})` el.style.backgroundSize = 'cover' el.style.backgroundPosition = 'center center' } html = html.replace(m[0], '') } this._renderTo('.cover-main', html) sticky() } proto._updateRender = function () { // render name link renderNameLink(this) } } export function initRender (vm) { const config = vm.config // Init markdown compiler vm.compiler = new Compiler(config, vm.router) const id = config.el || '#app' const navEl = dom.find('nav') || dom.create('nav') const el = dom.find(id) let html = '' let navAppendToTarget = dom.body if (el) { if (config.repo) { html += tpl.corner(config.repo) } if (config.coverpage) { html += tpl.cover() } html += tpl.main(config) // Render main app vm._renderTo(el, html, true) } else { vm.rendered = true } if (config.mergeNavbar && isMobile) { navAppendToTarget = dom.find('.sidebar') } else { navEl.classList.add('app-nav') if (!config.repo) { navEl.classList.add('no-badge') } } // Add nav dom.before(navAppendToTarget, navEl) if (config.themeColor) { dom.$.head.appendChild( dom.create('div', tpl.theme(config.themeColor)).firstElementChild ) // Polyfll cssVars(config.themeColor) } vm._updateRender() dom.toggleClass(dom.body, 'ready') }