mirror of https://github.com/IoTcat/docsify.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
6.4 KiB
263 lines
6.4 KiB
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, inBrowser} from '../util/env' |
|
import {isPrimitive} from '../util/core' |
|
import {scrollActiveSidebar, scroll2Top} from '../event/scroll' |
|
import {prerenderEmbed} from './embed' |
|
|
|
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) { |
|
html = '<h1>404 - Not found</h1>' |
|
} |
|
|
|
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)) |
|
if (this.config.loadNavbar) { |
|
getAndActive(this.router, 'nav') |
|
} |
|
} |
|
|
|
proto._renderMain = function (text, opt = {}, next) { |
|
if (!text) { |
|
return renderMain.call(this, text) |
|
} |
|
|
|
callHook(this, 'beforeEach', text, result => { |
|
let html |
|
const callback = () => { |
|
if (opt.updatedAt) { |
|
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated) |
|
} |
|
|
|
callHook(this, 'afterEach', html, text => renderMain.call(this, text)) |
|
} |
|
if (this.isHTML) { |
|
html = this.result = text |
|
callback() |
|
next() |
|
} else { |
|
prerenderEmbed( |
|
{ |
|
compiler: this.compiler, |
|
raw: result |
|
}, |
|
tokens => { |
|
html = this.compiler.compile(tokens) |
|
callback() |
|
next() |
|
} |
|
) |
|
} |
|
}) |
|
} |
|
|
|
proto._renderCover = function (text, coverOnly) { |
|
const el = dom.getNode('.cover') |
|
|
|
dom.toggleClass(dom.getNode('main'), coverOnly ? 'add' : 'remove', 'hidden') |
|
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) |
|
if (inBrowser) { |
|
window.__current_docsify_compiler__ = vm.compiler |
|
} |
|
|
|
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 |
|
if (config.loadNavbar) { |
|
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') |
|
}
|
|
|