refactor(router): dupports multiple mode
This commit is contained in:
parent
2a21c40f17
commit
2c7041c8fb
20 changed files with 377 additions and 220 deletions
|
|
@ -23,7 +23,7 @@
|
||||||
"build": "rm -rf lib themes && node build/build.js && mkdir lib/themes && mkdir themes && node build/build-css.js",
|
"build": "rm -rf lib themes && node build/build.js && mkdir lib/themes && mkdir themes && node build/build-css.js",
|
||||||
"dev:build": "rm -rf lib themes && mkdir themes && node build/build.js --dev && node build/build-css.js --dev",
|
"dev:build": "rm -rf lib themes && mkdir themes && node build/build.js --dev && node build/build-css.js --dev",
|
||||||
"dev": "node app.js & nodemon -w src -e js,css --exec 'npm run dev:build'",
|
"dev": "node app.js & nodemon -w src -e js,css --exec 'npm run dev:build'",
|
||||||
"test": "eslint src"
|
"test": "eslint src --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"marked": "^0.3.6",
|
"marked": "^0.3.6",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { isMobile } from '../util/env'
|
import { isMobile } from '../util/env'
|
||||||
import * as dom from '../util/dom'
|
import * as dom from '../util/dom'
|
||||||
import { parse } from '../route/hash'
|
import { parse } from '../router/hash'
|
||||||
|
|
||||||
const nav = {}
|
const nav = {}
|
||||||
let hoverOver = false
|
let hoverOver = false
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { isMobile } from '../util/env'
|
import { isMobile } from '../util/env'
|
||||||
import * as dom from '../util/dom'
|
import * as dom from '../util/dom'
|
||||||
import { getHash } from '../route/hash'
|
import { getHash } from '../router/hash'
|
||||||
|
|
||||||
const title = dom.$.title
|
const title = dom.$.title
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { get } from './ajax'
|
import { get } from './ajax'
|
||||||
import { callHook } from '../init/lifecycle'
|
import { callHook } from '../init/lifecycle'
|
||||||
import { getParentPath } from '../route/util'
|
import { getParentPath } from '../router/util'
|
||||||
import { noop } from '../util/core'
|
import { noop } from '../util/core'
|
||||||
|
|
||||||
function loadNested (path, file, next, vm, first) {
|
function loadNested (path, file, next, vm, first) {
|
||||||
|
|
@ -9,7 +9,7 @@ function loadNested (path, file, next, vm, first) {
|
||||||
|
|
||||||
if (!path) return
|
if (!path) return
|
||||||
|
|
||||||
get(vm.$getFile(path + file))
|
get(vm.router.getFile(path + file))
|
||||||
.then(next, _ => loadNested(path, file, next, vm))
|
.then(next, _ => loadNested(path, file, next, vm))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ export function fetchMixin (proto) {
|
||||||
// Abort last request
|
// Abort last request
|
||||||
last && last.abort && last.abort()
|
last && last.abort && last.abort()
|
||||||
|
|
||||||
last = get(this.$getFile(path), true)
|
last = get(this.router.getFile(path), true)
|
||||||
|
|
||||||
// Current page is html
|
// Current page is html
|
||||||
this.isHTML = /\.html$/g.test(path)
|
this.isHTML = /\.html$/g.test(path)
|
||||||
|
|
@ -47,7 +47,7 @@ export function fetchMixin (proto) {
|
||||||
proto._fetchCover = function () {
|
proto._fetchCover = function () {
|
||||||
const { coverpage } = this.config
|
const { coverpage } = this.config
|
||||||
const root = getParentPath(this.route.path)
|
const root = getParentPath(this.route.path)
|
||||||
const path = this.$getFile(root + coverpage)
|
const path = this.router.getFile(root + coverpage)
|
||||||
|
|
||||||
if (this.route.path !== '/' || !coverpage) {
|
if (this.route.path !== '/' || !coverpage) {
|
||||||
this._renderCover()
|
this._renderCover()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import * as util from './util'
|
import * as util from './util'
|
||||||
import * as dom from './util/dom'
|
import * as dom from './util/dom'
|
||||||
import * as render from './render/compiler'
|
import * as render from './render/compiler'
|
||||||
import * as route from './route/hash'
|
import * as route from './router/hash'
|
||||||
import { slugify } from './render/slugify'
|
import { slugify } from './render/slugify'
|
||||||
import { get } from './fetch/ajax'
|
import { get } from './fetch/ajax'
|
||||||
import marked from 'marked'
|
import marked from 'marked'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { initMixin } from './init'
|
import { initMixin } from './init'
|
||||||
import { routeMixin } from './route'
|
import { routerMixin } from './router'
|
||||||
import { renderMixin } from './render'
|
import { renderMixin } from './render'
|
||||||
import { fetchMixin } from './fetch'
|
import { fetchMixin } from './fetch'
|
||||||
import { eventMixin } from './event'
|
import { eventMixin } from './event'
|
||||||
|
|
@ -12,7 +12,7 @@ function Docsify () {
|
||||||
const proto = Docsify.prototype
|
const proto = Docsify.prototype
|
||||||
|
|
||||||
initMixin(proto)
|
initMixin(proto)
|
||||||
routeMixin(proto)
|
routerMixin(proto)
|
||||||
renderMixin(proto)
|
renderMixin(proto)
|
||||||
fetchMixin(proto)
|
fetchMixin(proto)
|
||||||
eventMixin(proto)
|
eventMixin(proto)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import config from '../config'
|
import config from '../config'
|
||||||
import { initLifecycle, callHook } from './lifecycle'
|
import { initLifecycle, callHook } from './lifecycle'
|
||||||
import { initRender } from '../render'
|
import { initRender } from '../render'
|
||||||
import { initRoute } from '../route'
|
import { initRouter } from '../router'
|
||||||
import { initEvent } from '../event'
|
import { initEvent } from '../event'
|
||||||
import { initFetch } from '../fetch'
|
import { initFetch } from '../fetch'
|
||||||
import { isFn } from '../util/core'
|
import { isFn } from '../util/core'
|
||||||
|
|
@ -14,9 +14,9 @@ export function initMixin (proto) {
|
||||||
initLifecycle(vm) // Init hooks
|
initLifecycle(vm) // Init hooks
|
||||||
initPlugin(vm) // Install plugins
|
initPlugin(vm) // Install plugins
|
||||||
callHook(vm, 'init')
|
callHook(vm, 'init')
|
||||||
|
initRouter(vm) // Add router
|
||||||
initRender(vm) // Render base DOM
|
initRender(vm) // Render base DOM
|
||||||
initEvent(vm) // Bind events
|
initEvent(vm) // Bind events
|
||||||
initRoute(vm) // Add hashchange eventListener
|
|
||||||
initFetch(vm) // Fetch data
|
initFetch(vm) // Fetch data
|
||||||
callHook(vm, 'mounted')
|
callHook(vm, 'mounted')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,160 +4,165 @@ import { helper as helperTpl, tree as treeTpl } from './tpl'
|
||||||
import { genTree } from './gen-tree'
|
import { genTree } from './gen-tree'
|
||||||
import { slugify } from './slugify'
|
import { slugify } from './slugify'
|
||||||
import { emojify } from './emojify'
|
import { emojify } from './emojify'
|
||||||
import { toURL, parse } from '../route/hash'
|
import { getBasePath, isAbsolutePath, getPath } from '../router/util'
|
||||||
import { getBasePath, isAbsolutePath, getPath } from '../route/util'
|
|
||||||
import { isFn, merge, cached } from '../util/core'
|
import { isFn, merge, cached } from '../util/core'
|
||||||
|
|
||||||
let markdownCompiler = marked
|
export class Compiler {
|
||||||
let contentBase = ''
|
constructor (config, router) {
|
||||||
let currentPath = ''
|
this.config = config
|
||||||
let linkTarget = '_blank'
|
this.router = router
|
||||||
let renderer = new marked.Renderer()
|
this.cacheTree = {}
|
||||||
const cacheTree = {}
|
this.toc = []
|
||||||
let toc = []
|
this.linkTarget = config.externalLinkTarget || '_blank'
|
||||||
|
this.contentBase = getBasePath(config.base)
|
||||||
|
|
||||||
/**
|
const renderer = this._initRenderer()
|
||||||
* Compile markdown content
|
let runner
|
||||||
*/
|
const mdConf = config.markdown || {}
|
||||||
export const markdown = cached(text => {
|
|
||||||
let html = ''
|
|
||||||
|
|
||||||
if (!text) return text
|
if (isFn(mdConf)) {
|
||||||
|
runner = mdConf(marked, renderer)
|
||||||
|
} else {
|
||||||
|
marked.setOptions(merge(mdConf, {
|
||||||
|
renderer: merge(renderer, mdConf.renderer)
|
||||||
|
}))
|
||||||
|
runner = marked
|
||||||
|
}
|
||||||
|
|
||||||
html = markdownCompiler(text)
|
this.runner = cached(text => {
|
||||||
html = emojify(html)
|
let html = ''
|
||||||
slugify.clear()
|
|
||||||
|
|
||||||
return html
|
if (!text) return text
|
||||||
})
|
|
||||||
|
|
||||||
markdown.renderer = renderer
|
html = runner(text)
|
||||||
|
html = emojify(html)
|
||||||
|
slugify.clear()
|
||||||
|
|
||||||
markdown.init = function (config = {}, {
|
return html
|
||||||
base = window.location.pathname,
|
})
|
||||||
externalLinkTarget
|
|
||||||
}) {
|
|
||||||
contentBase = getBasePath(base)
|
|
||||||
linkTarget = externalLinkTarget || linkTarget
|
|
||||||
|
|
||||||
if (isFn(config)) {
|
|
||||||
markdownCompiler = config(marked, renderer)
|
|
||||||
} else {
|
|
||||||
renderer = merge(renderer, config.renderer)
|
|
||||||
marked.setOptions(merge(config, { renderer }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
markdown.update = function () {
|
|
||||||
currentPath = parse().path
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* render anchor tag
|
|
||||||
* @link https://github.com/chjj/marked#overriding-renderer-methods
|
|
||||||
*/
|
|
||||||
renderer.heading = function (text, level) {
|
|
||||||
const nextToc = { level, title: text }
|
|
||||||
|
|
||||||
if (/{docsify-ignore}/g.test(text)) {
|
|
||||||
text = text.replace('{docsify-ignore}', '')
|
|
||||||
nextToc.title = text
|
|
||||||
nextToc.ignoreSubHeading = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/{docsify-ignore-all}/g.test(text)) {
|
_initRenderer () {
|
||||||
text = text.replace('{docsify-ignore-all}', '')
|
const renderer = new marked.Renderer()
|
||||||
nextToc.title = text
|
const { linkTarget, router, toc } = this
|
||||||
nextToc.ignoreAllSubs = true
|
/**
|
||||||
|
* render anchor tag
|
||||||
|
* @link https://github.com/chjj/marked#overriding-renderer-methods
|
||||||
|
*/
|
||||||
|
renderer.heading = function (text, level) {
|
||||||
|
const nextToc = { level, title: text }
|
||||||
|
|
||||||
|
if (/{docsify-ignore}/g.test(text)) {
|
||||||
|
text = text.replace('{docsify-ignore}', '')
|
||||||
|
nextToc.title = text
|
||||||
|
nextToc.ignoreSubHeading = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/{docsify-ignore-all}/g.test(text)) {
|
||||||
|
text = text.replace('{docsify-ignore-all}', '')
|
||||||
|
nextToc.title = text
|
||||||
|
nextToc.ignoreAllSubs = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const slug = slugify(text)
|
||||||
|
const url = router.toURL(router.getCurrentPath(), { id: slug })
|
||||||
|
nextToc.slug = url
|
||||||
|
toc.push(nextToc)
|
||||||
|
|
||||||
|
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 = '') {
|
||||||
|
const hl = Prism.highlight(code, Prism.languages[lang] || Prism.languages.markup)
|
||||||
|
|
||||||
|
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${hl}</code></pre>`
|
||||||
|
}
|
||||||
|
renderer.link = function (href, title, text) {
|
||||||
|
let blank = ''
|
||||||
|
if (!/:|(\/{2})/.test(href)) {
|
||||||
|
href = router.toURL(href, null, router.getCurrentPath())
|
||||||
|
} else {
|
||||||
|
blank = ` target="${linkTarget}"`
|
||||||
|
}
|
||||||
|
if (title) {
|
||||||
|
title = ` title="${title}"`
|
||||||
|
}
|
||||||
|
return `<a href="${href}"${title || ''}${blank}>${text}</a>`
|
||||||
|
}
|
||||||
|
renderer.paragraph = function (text) {
|
||||||
|
if (/^!>/.test(text)) {
|
||||||
|
return helperTpl('tip', text)
|
||||||
|
} else if (/^\?>/.test(text)) {
|
||||||
|
return helperTpl('warn', text)
|
||||||
|
}
|
||||||
|
return `<p>${text}</p>`
|
||||||
|
}
|
||||||
|
renderer.image = function (href, title, text) {
|
||||||
|
let url = href
|
||||||
|
const titleHTML = title ? ` title="${title}"` : ''
|
||||||
|
|
||||||
|
if (!isAbsolutePath(href)) {
|
||||||
|
url = getPath(this.contentBase, href)
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<img src="${url}" data-origin="${href}" alt="${text}"${titleHTML}>`
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
const slug = slugify(text)
|
/**
|
||||||
const url = toURL(currentPath, { id: slug })
|
* Compile sidebar
|
||||||
nextToc.slug = url
|
*/
|
||||||
toc.push(nextToc)
|
sidebar (text, level) {
|
||||||
|
const currentPath = this.router.getCurrentPath()
|
||||||
|
let html = ''
|
||||||
|
|
||||||
return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${text}</span></a></h${level}>`
|
if (text) {
|
||||||
}
|
html = this.runner(text)
|
||||||
// highlight code
|
html = html.match(/<ul[^>]*>([\s\S]+)<\/ul>/g)[0]
|
||||||
renderer.code = function (code, lang = '') {
|
} else {
|
||||||
const hl = Prism.highlight(code, Prism.languages[lang] || Prism.languages.markup)
|
const tree = this.cacheTree[currentPath] || genTree(this.toc, level)
|
||||||
|
html = treeTpl(tree, '<ul>')
|
||||||
|
this.cacheTree[currentPath] = tree
|
||||||
|
}
|
||||||
|
|
||||||
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${hl}</code></pre>`
|
return html
|
||||||
}
|
|
||||||
renderer.link = function (href, title, text) {
|
|
||||||
let blank = ''
|
|
||||||
if (!/:|(\/{2})/.test(href)) {
|
|
||||||
href = toURL(href, null, currentPath)
|
|
||||||
} else {
|
|
||||||
blank = ` target="${linkTarget}"`
|
|
||||||
}
|
|
||||||
if (title) {
|
|
||||||
title = ` title="${title}"`
|
|
||||||
}
|
|
||||||
return `<a href="${href}"${title || ''}${blank}>${text}</a>`
|
|
||||||
}
|
|
||||||
renderer.paragraph = function (text) {
|
|
||||||
if (/^!>/.test(text)) {
|
|
||||||
return helperTpl('tip', text)
|
|
||||||
} else if (/^\?>/.test(text)) {
|
|
||||||
return helperTpl('warn', text)
|
|
||||||
}
|
|
||||||
return `<p>${text}</p>`
|
|
||||||
}
|
|
||||||
renderer.image = function (href, title, text) {
|
|
||||||
let url = href
|
|
||||||
const titleHTML = title ? ` title="${title}"` : ''
|
|
||||||
|
|
||||||
if (!isAbsolutePath(href)) {
|
|
||||||
url = getPath(contentBase, href)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<img src="${url}" data-origin="${href}" alt="${text}"${titleHTML}>`
|
/**
|
||||||
}
|
* Compile sub sidebar
|
||||||
|
*/
|
||||||
|
subSidebar (level) {
|
||||||
|
const currentPath = this.router.getCurrentPath()
|
||||||
|
const { cacheTree, toc } = this
|
||||||
|
|
||||||
/**
|
toc[0] && toc[0].ignoreAllSubs && (this.toc = [])
|
||||||
* Compile sidebar
|
|
||||||
*/
|
|
||||||
export function sidebar (text, level) {
|
|
||||||
let html = ''
|
|
||||||
|
|
||||||
if (text) {
|
|
||||||
html = markdown(text)
|
|
||||||
html = html.match(/<ul[^>]*>([\s\S]+)<\/ul>/g)[0]
|
|
||||||
} else {
|
|
||||||
const tree = cacheTree[currentPath] || genTree(toc, level)
|
|
||||||
html = treeTpl(tree, '<ul>')
|
|
||||||
cacheTree[currentPath] = tree
|
|
||||||
}
|
|
||||||
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile sub sidebar
|
|
||||||
*/
|
|
||||||
export function subSidebar (el, level) {
|
|
||||||
if (el) {
|
|
||||||
toc[0] && toc[0].ignoreAllSubs && (toc = [])
|
|
||||||
toc[0] && toc[0].level === 1 && toc.shift()
|
toc[0] && toc[0].level === 1 && toc.shift()
|
||||||
toc.forEach((node, i) => {
|
toc.forEach((node, i) => {
|
||||||
node.ignoreSubHeading && toc.splice(i, 1)
|
node.ignoreSubHeading && toc.splice(i, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
const tree = cacheTree[currentPath] || genTree(toc, level)
|
const tree = cacheTree[currentPath] || genTree(toc, level)
|
||||||
el.parentNode.innerHTML += treeTpl(tree, '<ul class="app-sub-sidebar">')
|
|
||||||
cacheTree[currentPath] = tree
|
cacheTree[currentPath] = tree
|
||||||
|
this.toc = []
|
||||||
|
return treeTpl(tree, '<ul class="app-sub-sidebar">')
|
||||||
|
}
|
||||||
|
|
||||||
|
article (text) {
|
||||||
|
return this.runner(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile cover page
|
||||||
|
*/
|
||||||
|
cover (text) {
|
||||||
|
const cacheToc = this.toc.slice()
|
||||||
|
const html = this.runner(text)
|
||||||
|
|
||||||
|
this.toc = cacheToc.slice()
|
||||||
|
|
||||||
|
return html
|
||||||
}
|
}
|
||||||
toc = []
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile cover page
|
|
||||||
*/
|
|
||||||
export function cover (text) {
|
|
||||||
const cacheToc = toc.slice()
|
|
||||||
const html = markdown(text)
|
|
||||||
|
|
||||||
toc = cacheToc.slice()
|
|
||||||
|
|
||||||
return html
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ import { getAndActive, sticky } from '../event/sidebar'
|
||||||
import { scrollActiveSidebar, scroll2Top } from '../event/scroll'
|
import { scrollActiveSidebar, scroll2Top } from '../event/scroll'
|
||||||
import cssVars from '../util/polyfill/css-vars'
|
import cssVars from '../util/polyfill/css-vars'
|
||||||
import * as tpl from './tpl'
|
import * as tpl from './tpl'
|
||||||
import { markdown, sidebar, subSidebar, cover } from './compiler'
|
import { Compiler } from './compiler'
|
||||||
import { callHook } from '../init/lifecycle'
|
import { callHook } from '../init/lifecycle'
|
||||||
import { getBasePath, getPath, isAbsolutePath } from '../route/util'
|
import { getBasePath, getPath, isAbsolutePath } from '../router/util'
|
||||||
import { isPrimitive } from '../util/core'
|
import { isPrimitive } from '../util/core'
|
||||||
import { isMobile } from '../util/env'
|
import { isMobile } from '../util/env'
|
||||||
import tinydate from 'tinydate'
|
import tinydate from 'tinydate'
|
||||||
|
|
@ -85,32 +85,34 @@ export function renderMixin (proto) {
|
||||||
proto._renderSidebar = function (text) {
|
proto._renderSidebar = function (text) {
|
||||||
const { maxLevel, subMaxLevel, autoHeader, loadSidebar } = this.config
|
const { maxLevel, subMaxLevel, autoHeader, loadSidebar } = this.config
|
||||||
|
|
||||||
this._renderTo('.sidebar-nav', sidebar(text, maxLevel))
|
this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel))
|
||||||
const active = getAndActive('.sidebar-nav', true, true)
|
const activeEl = getAndActive('.sidebar-nav', true, true)
|
||||||
subSidebar(loadSidebar ? active : '', subMaxLevel)
|
if (loadSidebar && activeEl) {
|
||||||
|
activeEl.parentNode.innerHTML += this.compiler.subSidebar(subMaxLevel)
|
||||||
|
}
|
||||||
// bind event
|
// bind event
|
||||||
this.activeLink = active
|
this.activeLink = activeEl
|
||||||
scrollActiveSidebar()
|
scrollActiveSidebar()
|
||||||
|
|
||||||
if (autoHeader && active) {
|
if (autoHeader && activeEl) {
|
||||||
const main = dom.getNode('#main')
|
const main = dom.getNode('#main')
|
||||||
const firstNode = main.children[0]
|
const firstNode = main.children[0]
|
||||||
if (firstNode && firstNode.tagName !== 'H1') {
|
if (firstNode && firstNode.tagName !== 'H1') {
|
||||||
const h1 = dom.create('h1')
|
const h1 = dom.create('h1')
|
||||||
h1.innerText = active.innerText
|
h1.innerText = activeEl.innerText
|
||||||
dom.before(main, h1)
|
dom.before(main, h1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proto._renderNav = function (text) {
|
proto._renderNav = function (text) {
|
||||||
text && this._renderTo('nav', markdown(text))
|
text && this._renderTo('nav', this.compiler.runner(text))
|
||||||
getAndActive('nav')
|
getAndActive('nav')
|
||||||
}
|
}
|
||||||
|
|
||||||
proto._renderMain = function (text, opt = {}) {
|
proto._renderMain = function (text, opt = {}) {
|
||||||
callHook(this, 'beforeEach', text, result => {
|
callHook(this, 'beforeEach', text, result => {
|
||||||
let html = this.isHTML ? result : markdown(result)
|
let html = this.isHTML ? result : this.compiler.runner(result)
|
||||||
if (opt.updatedAt) {
|
if (opt.updatedAt) {
|
||||||
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
|
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +129,7 @@ export function renderMixin (proto) {
|
||||||
}
|
}
|
||||||
dom.toggleClass(el, 'add', 'show')
|
dom.toggleClass(el, 'add', 'show')
|
||||||
|
|
||||||
let html = this.coverIsHTML ? text : cover(text)
|
let html = this.coverIsHTML ? text : this.compiler.cover(text)
|
||||||
const m = html.trim().match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$')
|
const m = html.trim().match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$')
|
||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
|
|
@ -152,7 +154,6 @@ export function renderMixin (proto) {
|
||||||
}
|
}
|
||||||
|
|
||||||
proto._updateRender = function () {
|
proto._updateRender = function () {
|
||||||
markdown.update()
|
|
||||||
// render name link
|
// render name link
|
||||||
renderNameLink(this)
|
renderNameLink(this)
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +163,7 @@ export function initRender (vm) {
|
||||||
const config = vm.config
|
const config = vm.config
|
||||||
|
|
||||||
// Init markdown compiler
|
// Init markdown compiler
|
||||||
markdown.init(config.markdown, config)
|
vm.compiler = new Compiler(config, vm.router)
|
||||||
|
|
||||||
const id = config.el || '#app'
|
const id = config.el || '#app'
|
||||||
const navEl = dom.find('nav') || dom.create('nav')
|
const navEl = dom.find('nav') || dom.create('nav')
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
import { normalize, parse } from './hash'
|
|
||||||
import { getBasePath, getPath, isAbsolutePath } from './util'
|
|
||||||
import { on } from '../util/dom'
|
|
||||||
|
|
||||||
function getAlias (path, alias) {
|
|
||||||
return alias[path] ? getAlias(alias[path], alias) : path
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileName (path) {
|
|
||||||
return /\.(md|html)$/g.test(path)
|
|
||||||
? path
|
|
||||||
: /\/$/g.test(path)
|
|
||||||
? `${path}README.md`
|
|
||||||
: `${path}.md`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function routeMixin (proto) {
|
|
||||||
proto.route = {}
|
|
||||||
proto.$getFile = function (path) {
|
|
||||||
const { config } = this
|
|
||||||
const base = getBasePath(config.basePath)
|
|
||||||
|
|
||||||
path = config.alias ? getAlias(path, config.alias) : path
|
|
||||||
path = getFileName(path)
|
|
||||||
path = path === '/README.md' ? (config.homepage || path) : path
|
|
||||||
path = isAbsolutePath(path) ? path : getPath(base, path)
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lastRoute = {}
|
|
||||||
|
|
||||||
export function initRoute (vm) {
|
|
||||||
normalize()
|
|
||||||
lastRoute = vm.route = parse()
|
|
||||||
vm._updateRender()
|
|
||||||
|
|
||||||
on('hashchange', _ => {
|
|
||||||
normalize()
|
|
||||||
vm.route = parse()
|
|
||||||
vm._updateRender()
|
|
||||||
|
|
||||||
if (lastRoute.path === vm.route.path) {
|
|
||||||
vm.$resetEvents()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.$fetch()
|
|
||||||
lastRoute = vm.route
|
|
||||||
})
|
|
||||||
}
|
|
||||||
8
src/core/router/history/abstract.js
Normal file
8
src/core/router/history/abstract.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { History } from './base'
|
||||||
|
|
||||||
|
export class AbstractHistory extends History {
|
||||||
|
constructor (config) {
|
||||||
|
super(config)
|
||||||
|
this.mode = 'abstract'
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/core/router/history/base.js
Normal file
44
src/core/router/history/base.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { getBasePath, getPath, isAbsolutePath } from '../util'
|
||||||
|
import { noop } from '../../util/core'
|
||||||
|
|
||||||
|
function getAlias (path, alias) {
|
||||||
|
return alias[path] ? getAlias(alias[path], alias) : path
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileName (path) {
|
||||||
|
return /\.(md|html)$/g.test(path)
|
||||||
|
? path
|
||||||
|
: /\/$/g.test(path)
|
||||||
|
? `${path}README.md`
|
||||||
|
: `${path}.md`
|
||||||
|
}
|
||||||
|
|
||||||
|
export class History {
|
||||||
|
constructor (config) {
|
||||||
|
this.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
onchange (cb = noop) {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
|
||||||
|
getFile (path) {
|
||||||
|
const { config } = this
|
||||||
|
const base = getBasePath(config.basePath)
|
||||||
|
|
||||||
|
path = config.alias ? getAlias(path, config.alias) : path
|
||||||
|
path = getFileName(path)
|
||||||
|
path = path === '/README.md' ? (config.homepage || path) : path
|
||||||
|
path = isAbsolutePath(path) ? path : getPath(base, path)
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentPath () {}
|
||||||
|
|
||||||
|
normalize () {}
|
||||||
|
|
||||||
|
parse () {}
|
||||||
|
|
||||||
|
toURL () {}
|
||||||
|
}
|
||||||
78
src/core/router/history/hash.js
Normal file
78
src/core/router/history/hash.js
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { History } from './base'
|
||||||
|
import { merge, cached, noop } from '../../util/core'
|
||||||
|
import { parseQuery, stringifyQuery, cleanPath } from '../util'
|
||||||
|
import { on } from '../../util/dom'
|
||||||
|
|
||||||
|
function replaceHash (path) {
|
||||||
|
const i = location.href.indexOf('#')
|
||||||
|
location.replace(
|
||||||
|
location.href.slice(0, i >= 0 ? i : 0) + '#' + path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const replaceSlug = cached(path => {
|
||||||
|
return path.replace('#', '?id=')
|
||||||
|
})
|
||||||
|
|
||||||
|
export class HashHistory extends History {
|
||||||
|
constructor (config) {
|
||||||
|
super(config)
|
||||||
|
this.mode = 'hash'
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentPath () {
|
||||||
|
// We can't use location.hash here because it's not
|
||||||
|
// consistent across browsers - Firefox will pre-decode it!
|
||||||
|
const href = location.href
|
||||||
|
const index = href.indexOf('#')
|
||||||
|
return index === -1 ? '' : href.slice(index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
onchange (cb = noop) {
|
||||||
|
on('hashchange', cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize () {
|
||||||
|
let path = this.getCurrentPath()
|
||||||
|
|
||||||
|
path = replaceSlug(path)
|
||||||
|
|
||||||
|
if (path.charAt(0) === '/') return replaceHash(path)
|
||||||
|
replaceHash('/' + path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the url
|
||||||
|
* @param {string} [path=location.herf]
|
||||||
|
* @return {object} { path, query }
|
||||||
|
*/
|
||||||
|
parse (path = location.href) {
|
||||||
|
let query = ''
|
||||||
|
|
||||||
|
const queryIndex = path.indexOf('?')
|
||||||
|
if (queryIndex >= 0) {
|
||||||
|
query = path.slice(queryIndex + 1)
|
||||||
|
path = path.slice(0, queryIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashIndex = path.indexOf('#')
|
||||||
|
if (hashIndex) {
|
||||||
|
path = path.slice(hashIndex + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { path, query: parseQuery(query) }
|
||||||
|
}
|
||||||
|
|
||||||
|
toURL (path, params, currentRoute) {
|
||||||
|
const local = currentRoute && path[0] === '#'
|
||||||
|
const route = this.parse(replaceSlug(path))
|
||||||
|
|
||||||
|
route.query = merge({}, route.query, params)
|
||||||
|
path = route.path + stringifyQuery(route.query)
|
||||||
|
path = path.replace(/\.md(\?)|\.md$/, '$1')
|
||||||
|
|
||||||
|
if (local) path = currentRoute + path
|
||||||
|
|
||||||
|
return cleanPath('#/' + path)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/core/router/history/html5.js
Normal file
8
src/core/router/history/html5.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { History } from './base'
|
||||||
|
|
||||||
|
export class HTML5History extends History {
|
||||||
|
constructor (config) {
|
||||||
|
super(config)
|
||||||
|
this.mode = 'history'
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/core/router/index.js
Normal file
44
src/core/router/index.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { AbstractHistory } from './history/abstract'
|
||||||
|
import { HashHistory } from './history/hash'
|
||||||
|
import { HTML5History } from './history/html5'
|
||||||
|
import { supportsPushState, inBrowser } from '../util/env'
|
||||||
|
|
||||||
|
export function routerMixin (proto) {
|
||||||
|
proto.route = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastRoute = {}
|
||||||
|
|
||||||
|
export function initRouter (vm) {
|
||||||
|
const config = vm.config
|
||||||
|
const mode = config.routerMode || 'hash'
|
||||||
|
let router
|
||||||
|
|
||||||
|
if (mode === 'history' && supportsPushState) {
|
||||||
|
router = new HTML5History(config)
|
||||||
|
} else if (!inBrowser || mode === 'abstract') {
|
||||||
|
router = new AbstractHistory(config)
|
||||||
|
} else {
|
||||||
|
router = new HashHistory(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.router = router
|
||||||
|
|
||||||
|
router.normalize()
|
||||||
|
lastRoute = vm.route = router.parse()
|
||||||
|
vm._updateRender()
|
||||||
|
|
||||||
|
router.onchange(_ => {
|
||||||
|
router.normalize()
|
||||||
|
vm.route = router.parse()
|
||||||
|
vm._updateRender()
|
||||||
|
|
||||||
|
if (lastRoute.path === vm.route.path) {
|
||||||
|
vm.$resetEvents()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.$fetch()
|
||||||
|
lastRoute = vm.route
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -3,3 +3,20 @@ export const UA = window.navigator.userAgent.toLowerCase()
|
||||||
export const isIE = UA && /msie|trident/.test(UA)
|
export const isIE = UA && /msie|trident/.test(UA)
|
||||||
|
|
||||||
export const isMobile = document.body.clientWidth <= 600
|
export const isMobile = document.body.clientWidth <= 600
|
||||||
|
|
||||||
|
export const inBrowser = typeof window !== 'undefined'
|
||||||
|
|
||||||
|
export const supportsPushState = inBrowser && (function () {
|
||||||
|
const ua = window.navigator.userAgent
|
||||||
|
|
||||||
|
if (
|
||||||
|
(ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
|
||||||
|
ua.indexOf('Mobile Safari') !== -1 &&
|
||||||
|
ua.indexOf('Chrome') === -1 &&
|
||||||
|
ua.indexOf('Windows Phone') === -1
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.history && 'pushState' in window.history
|
||||||
|
})()
|
||||||
|
|
|
||||||
|
|
@ -7,24 +7,28 @@ function appendScript () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function init (id) {
|
function init (id) {
|
||||||
if (!window.ga) {
|
let ga = window.ga
|
||||||
|
|
||||||
|
if (!ga) {
|
||||||
appendScript()
|
appendScript()
|
||||||
window.ga = window.ga || function () {
|
ga = ga || function () {
|
||||||
(window.ga.q = window.ga.q || []).push(arguments)
|
(ga.q = ga.q || []).push(arguments)
|
||||||
}
|
}
|
||||||
window.ga.l = Number(new Date())
|
ga.l = Number(new Date())
|
||||||
window.ga('create', id, 'auto')
|
ga('create', id, 'auto')
|
||||||
}
|
}
|
||||||
|
return ga
|
||||||
}
|
}
|
||||||
|
|
||||||
function collect () {
|
function collect () {
|
||||||
init(window.$docsify.ga)
|
const ga = init($docsify.ga)
|
||||||
window.ga('set', 'page', location.hash)
|
|
||||||
window.ga('send', 'pageview')
|
ga('set', 'page', location.hash)
|
||||||
|
ga('send', 'pageview')
|
||||||
}
|
}
|
||||||
|
|
||||||
const install = function (hook) {
|
const install = function (hook) {
|
||||||
if (!window.$docsify.ga) {
|
if (!$docsify.ga) {
|
||||||
console.error('[Docsify] ga is required.')
|
console.error('[Docsify] ga is required.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -32,4 +36,4 @@ const install = function (hook) {
|
||||||
hook.beforeEach(collect)
|
hook.beforeEach(collect)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.$docsify.plugins = [].concat(install, window.$docsify.plugins)
|
$docsify.plugins = [].concat(install, $docsify.plugins)
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ export function init (config, vm) {
|
||||||
if (INDEXS[path]) return count++
|
if (INDEXS[path]) return count++
|
||||||
|
|
||||||
helper
|
helper
|
||||||
.get(vm.$getFile(path))
|
.get(vm.router.getFile(path))
|
||||||
.then(result => {
|
.then(result => {
|
||||||
INDEXS[path] = genIndex(path, result)
|
INDEXS[path] = genIndex(path, result)
|
||||||
len === ++count && saveData(config.maxAge)
|
len === ++count && saveData(config.maxAge)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue