mirror of
https://github.com/zhigang1992/docsify.git
synced 2026-04-29 09:55:54 +08:00
refactor(core): fix route path
This commit is contained in:
@@ -1,6 +1,14 @@
|
|||||||
import { isMobile } from '../util/env'
|
import { isMobile } from '../util/env'
|
||||||
import { body, on } from '../util/dom'
|
import { body, on } from '../util/dom'
|
||||||
import * as sidebar from './sidebar'
|
import * as sidebar from './sidebar'
|
||||||
|
import { scrollIntoView } from './scroll'
|
||||||
|
|
||||||
|
export function eventMixin (proto) {
|
||||||
|
proto.$resetEvents = function () {
|
||||||
|
scrollIntoView(this.route.query.id)
|
||||||
|
sidebar.getAndActive('nav')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function initEvent (vm) {
|
export function initEvent (vm) {
|
||||||
// Bind toggle button
|
// Bind toggle button
|
||||||
|
|||||||
@@ -1,5 +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'
|
||||||
|
|
||||||
export function scrollActiveSidebar () {
|
export function scrollActiveSidebar () {
|
||||||
if (isMobile) return
|
if (isMobile) return
|
||||||
@@ -21,8 +22,7 @@ export function scrollActiveSidebar () {
|
|||||||
let href = a.getAttribute('href')
|
let href = a.getAttribute('href')
|
||||||
|
|
||||||
if (href !== '/') {
|
if (href !== '/') {
|
||||||
const match = href.match('#([^#]+)$')
|
href = parse(href).query.id
|
||||||
if (match && match.length) href = match[0].slice(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav[decodeURIComponent(href)] = li
|
nav[decodeURIComponent(href)] = li
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export function get (url, hasBar = false) {
|
|||||||
return {
|
return {
|
||||||
then: function (success, error = noop) {
|
then: function (success, error = noop) {
|
||||||
if (hasBar) {
|
if (hasBar) {
|
||||||
const id = setInterval(_ => progressbar({}), 500)
|
const id = setInterval(_ => progressbar({
|
||||||
|
step: Math.floor(Math.random() * 5 + 1)
|
||||||
|
}), 500)
|
||||||
|
|
||||||
on('progress', progressbar)
|
on('progress', progressbar)
|
||||||
on('loadend', evt => {
|
on('loadend', evt => {
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ import { get } from './ajax'
|
|||||||
import { callHook } from '../init/lifecycle'
|
import { callHook } from '../init/lifecycle'
|
||||||
import { getRoot } from '../route/util'
|
import { getRoot } from '../route/util'
|
||||||
import { noop } from '../util/core'
|
import { noop } from '../util/core'
|
||||||
import { scrollIntoView } from '../event/scroll'
|
|
||||||
import { getAndActive } from '../event/sidebar'
|
|
||||||
|
|
||||||
export function fetchMixin (Docsify) {
|
export function fetchMixin (proto) {
|
||||||
let last
|
let last
|
||||||
|
proto._fetch = function (cb = noop) {
|
||||||
Docsify.prototype._fetch = function (cb = noop) {
|
|
||||||
const { path } = this.route
|
const { path } = this.route
|
||||||
const { loadNavbar, loadSidebar } = this.config
|
const { loadNavbar, loadSidebar } = this.config
|
||||||
const root = getRoot(path)
|
const root = getRoot(path)
|
||||||
@@ -42,7 +39,7 @@ export function fetchMixin (Docsify) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Docsify.prototype._fetchCover = function () {
|
proto._fetchCover = function () {
|
||||||
const { coverpage } = this.config
|
const { coverpage } = this.config
|
||||||
const root = getRoot(this.route.path)
|
const root = getRoot(this.route.path)
|
||||||
|
|
||||||
@@ -54,13 +51,16 @@ export function fetchMixin (Docsify) {
|
|||||||
get(this.$getFile(root + coverpage))
|
get(this.$getFile(root + coverpage))
|
||||||
.then(text => this._renderCover(text))
|
.then(text => this._renderCover(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proto.$fetch = function () {
|
||||||
|
this._fetchCover()
|
||||||
|
this._fetch(result => {
|
||||||
|
this.$resetEvents()
|
||||||
|
callHook(this, 'doneEach')
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initFetch (vm) {
|
export function initFetch (vm) {
|
||||||
vm._fetchCover(vm)
|
vm.$fetch()
|
||||||
vm._fetch(result => {
|
|
||||||
scrollIntoView(vm.route.query.id)
|
|
||||||
getAndActive('nav')
|
|
||||||
callHook(vm, 'doneEach')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,20 @@ import { initMixin } from './init'
|
|||||||
import { routeMixin } from './route'
|
import { routeMixin } from './route'
|
||||||
import { renderMixin } from './render'
|
import { renderMixin } from './render'
|
||||||
import { fetchMixin } from './fetch'
|
import { fetchMixin } from './fetch'
|
||||||
|
import { eventMixin } from './event'
|
||||||
import initGlobalAPI from './global-api'
|
import initGlobalAPI from './global-api'
|
||||||
|
|
||||||
function Docsify () {
|
function Docsify () {
|
||||||
this._init()
|
this._init()
|
||||||
}
|
}
|
||||||
|
|
||||||
initMixin(Docsify)
|
const proto = Docsify.prototype
|
||||||
routeMixin(Docsify)
|
|
||||||
renderMixin(Docsify)
|
initMixin(proto)
|
||||||
fetchMixin(Docsify)
|
routeMixin(proto)
|
||||||
|
renderMixin(proto)
|
||||||
|
fetchMixin(proto)
|
||||||
|
eventMixin(proto)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global API
|
* Global API
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { initEvent } from '../event'
|
|||||||
import { initFetch } from '../fetch'
|
import { initFetch } from '../fetch'
|
||||||
import { isFn } from '../util/core'
|
import { isFn } from '../util/core'
|
||||||
|
|
||||||
export function initMixin (Docsify) {
|
export function initMixin (proto) {
|
||||||
Docsify.prototype._init = function () {
|
proto._init = function () {
|
||||||
const vm = this
|
const vm = this
|
||||||
vm.config = config || {}
|
vm.config = config || {}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,15 @@ import { helper as helperTpl, tree as treeTpl } from './tpl'
|
|||||||
import { genTree } from './gen-tree'
|
import { genTree } from './gen-tree'
|
||||||
import { slugify, clearSlugCache } from './slugify'
|
import { slugify, clearSlugCache } from './slugify'
|
||||||
import { emojify } from './emojify'
|
import { emojify } from './emojify'
|
||||||
import { toURL } from '../route/hash'
|
import { toURL, parse } from '../route/hash'
|
||||||
import { isFn, merge, cached, noop } from '../util/core'
|
import { getBasePath, getPath } from '../route/util'
|
||||||
|
import { isFn, merge, cached } from '../util/core'
|
||||||
|
|
||||||
let markdownCompiler = marked
|
let markdownCompiler = marked
|
||||||
let contentBase = ''
|
let contentBase = ''
|
||||||
|
let currentPath = ''
|
||||||
let renderer = new marked.Renderer()
|
let renderer = new marked.Renderer()
|
||||||
|
const TOC = {}
|
||||||
let toc = []
|
let toc = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +33,8 @@ export const markdown = cached(text => {
|
|||||||
markdown.renderer = renderer
|
markdown.renderer = renderer
|
||||||
|
|
||||||
markdown.init = function (config = {}, base = window.location.pathname) {
|
markdown.init = function (config = {}, base = window.location.pathname) {
|
||||||
contentBase = base
|
contentBase = getBasePath(base)
|
||||||
|
currentPath = parse().path
|
||||||
|
|
||||||
if (isFn(config)) {
|
if (isFn(config)) {
|
||||||
markdownCompiler = config(marked, renderer)
|
markdownCompiler = config(marked, renderer)
|
||||||
@@ -40,13 +44,17 @@ markdown.init = function (config = {}, base = window.location.pathname) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
markdown.update = function () {
|
||||||
|
currentPath = parse().path
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* render anchor tag
|
* render anchor tag
|
||||||
* @link https://github.com/chjj/marked#overriding-renderer-methods
|
* @link https://github.com/chjj/marked#overriding-renderer-methods
|
||||||
*/
|
*/
|
||||||
renderer.heading = function (text, level) {
|
renderer.heading = function (text, level) {
|
||||||
const slug = slugify(text)
|
const slug = slugify(text)
|
||||||
const url = toURL(contentBase, { id: slug })
|
const url = toURL(currentPath, { id: slug })
|
||||||
|
|
||||||
toc.push({ level, slug: url, title: text })
|
toc.push({ level, slug: url, title: text })
|
||||||
|
|
||||||
@@ -60,8 +68,7 @@ renderer.code = function (code, lang = '') {
|
|||||||
}
|
}
|
||||||
renderer.link = function (href, title, text) {
|
renderer.link = function (href, title, text) {
|
||||||
if (!/:|(\/{2})/.test(href)) {
|
if (!/:|(\/{2})/.test(href)) {
|
||||||
// TODO
|
href = toURL(href)
|
||||||
href = `#/${href}`.replace(/\/+/g, '/')
|
|
||||||
}
|
}
|
||||||
return `<a href="${href}" title="${title || ''}">${text}</a>`
|
return `<a href="${href}" title="${title || ''}">${text}</a>`
|
||||||
}
|
}
|
||||||
@@ -74,12 +81,10 @@ renderer.paragraph = function (text) {
|
|||||||
return `<p>${text}</p>`
|
return `<p>${text}</p>`
|
||||||
}
|
}
|
||||||
renderer.image = function (href, title, text) {
|
renderer.image = function (href, title, text) {
|
||||||
// TODO
|
const url = getPath(contentBase, href)
|
||||||
// get base path
|
const titleHTML = title ? ` title="${title}"` : ''
|
||||||
// const url = /:|(\/{2})/.test(href) ? href : ($docsify.basePath + href).replace(/\/+/g, '/')
|
|
||||||
// const titleHTML = title ? ` title="${title}"` : ''
|
|
||||||
|
|
||||||
// return `<img src="${url}" alt="${text}"${titleHTML} />`
|
return `<img src="${url}" data-origin="${href}" alt="${text}"${titleHTML}>`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,8 +110,11 @@ export function sidebar (text, level) {
|
|||||||
export function subSidebar (el, level) {
|
export function subSidebar (el, level) {
|
||||||
if (el) {
|
if (el) {
|
||||||
toc[0] && toc[0].level === 1 && toc.shift()
|
toc[0] && toc[0].level === 1 && toc.shift()
|
||||||
const tree = genTree(toc, level)
|
const tree = genTree(TOC[currentPath] || toc, level)
|
||||||
el.parentNode.innerHTML += treeTpl(tree, '<ul>')
|
el.parentNode.innerHTML += treeTpl(tree, '<ul class="app-sub-sidebar">')
|
||||||
|
}
|
||||||
|
if (toc.length) {
|
||||||
|
TOC[currentPath] = toc.slice()
|
||||||
}
|
}
|
||||||
toc = []
|
toc = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
import * as dom from '../util/dom'
|
import * as dom from '../util/dom'
|
||||||
import { getAndActive, sticky } from '../event/sidebar'
|
import { getAndActive, sticky } from '../event/sidebar'
|
||||||
import { scrollActiveSidebar } 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 { markdown, sidebar, subSidebar, cover } from './compiler'
|
||||||
import { callHook } from '../init/lifecycle'
|
import { callHook } from '../init/lifecycle'
|
||||||
|
import { getBasePath, getPath } from '../route/util'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
window.__EXECUTE_RESULT__ = new Function('return ' + code)()
|
||||||
|
}
|
||||||
|
|
||||||
function renderMain (html) {
|
function renderMain (html) {
|
||||||
if (!html) {
|
if (!html) {
|
||||||
@@ -14,11 +25,21 @@ function renderMain (html) {
|
|||||||
this._renderTo('.markdown-section', html)
|
this._renderTo('.markdown-section', html)
|
||||||
// Render sidebar with the TOC
|
// Render sidebar with the TOC
|
||||||
!this.config.loadSidebar && this._renderSidebar()
|
!this.config.loadSidebar && this._renderSidebar()
|
||||||
|
// execute script
|
||||||
|
this.config.executeScript && executeScript()
|
||||||
|
|
||||||
|
if (!this.config.executeScript
|
||||||
|
&& typeof window.Vue !== 'undefined'
|
||||||
|
&& !executeScript()) {
|
||||||
|
window.__EXECUTE_RESULT__ = new window.Vue().$mount('#main')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.config.auto2top) {
|
||||||
|
setTimeout(() => scroll2Top(this.config.auto2top), 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderMixin (Docsify) {
|
export function renderMixin (proto) {
|
||||||
const proto = Docsify.prototype
|
|
||||||
|
|
||||||
proto._renderTo = function (el, content, replace) {
|
proto._renderTo = function (el, content, replace) {
|
||||||
const node = dom.getNode(el)
|
const node = dom.getNode(el)
|
||||||
if (node) node[replace ? 'outerHTML' : 'innerHTML'] = content
|
if (node) node[replace ? 'outerHTML' : 'innerHTML'] = content
|
||||||
@@ -54,14 +75,14 @@ export function renderMixin (Docsify) {
|
|||||||
dom.toggleClass(el, 'add', 'show')
|
dom.toggleClass(el, 'add', 'show')
|
||||||
|
|
||||||
let html = cover(text)
|
let html = cover(text)
|
||||||
const m = html.trim().match('<p><img[^s]+src="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$')
|
const m = html.trim().match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$')
|
||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
if (m[2] === 'color') {
|
if (m[2] === 'color') {
|
||||||
el.style.background = m[1] + (m[3] || '')
|
el.style.background = m[1] + (m[3] || '')
|
||||||
} else {
|
} else {
|
||||||
dom.toggleClass(el, 'add', 'has-mask')
|
dom.toggleClass(el, 'add', 'has-mask')
|
||||||
el.style.backgroundImage = `url(${m[1]})`
|
el.style.backgroundImage = `url(${getPath(getBasePath(this.config.basePath), m[1])})`
|
||||||
}
|
}
|
||||||
html = html.replace(m[0], '')
|
html = html.replace(m[0], '')
|
||||||
}
|
}
|
||||||
@@ -69,13 +90,17 @@ export function renderMixin (Docsify) {
|
|||||||
this._renderTo('.cover-main', html)
|
this._renderTo('.cover-main', html)
|
||||||
sticky()
|
sticky()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proto._updateRender = function () {
|
||||||
|
markdown.update()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initRender (vm) {
|
export function initRender (vm) {
|
||||||
const config = vm.config
|
const config = vm.config
|
||||||
|
|
||||||
// Init markdown compiler
|
// Init markdown compiler
|
||||||
markdown.init(vm.config.markdown)
|
markdown.init(config.markdown, config.basePath)
|
||||||
|
|
||||||
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,5 +1,4 @@
|
|||||||
import * as dom from '../util/dom'
|
import * as dom from '../util/dom'
|
||||||
import { isPrimitive } from '../util/core'
|
|
||||||
|
|
||||||
let barEl
|
let barEl
|
||||||
let timeId
|
let timeId
|
||||||
@@ -22,11 +21,8 @@ export default function ({ loaded, total, step }) {
|
|||||||
|
|
||||||
!barEl && init()
|
!barEl && init()
|
||||||
|
|
||||||
if (!isPrimitive(step)) {
|
|
||||||
step = Math.floor(Math.random() * 5 + 1)
|
|
||||||
}
|
|
||||||
if (step) {
|
if (step) {
|
||||||
num = parseInt(barEl.style.width, 10) + step
|
num = parseInt(barEl.style.width || 0, 10) + step
|
||||||
num = num > 80 ? 80 : num
|
num = num > 80 ? 80 : num
|
||||||
} else {
|
} else {
|
||||||
num = Math.floor(loaded / total * 100)
|
num = Math.floor(loaded / total * 100)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function main (config) {
|
|||||||
|
|
||||||
return (isMobile ? `${aside}<main>` : `<main>${aside}`) +
|
return (isMobile ? `${aside}<main>` : `<main>${aside}`) +
|
||||||
'<section class="content">' +
|
'<section class="content">' +
|
||||||
'<article class="markdown-section"></article>' +
|
'<article class="markdown-section" id="main"></article>' +
|
||||||
'</section>' +
|
'</section>' +
|
||||||
'</main>'
|
'</main>'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { merge } from '../util/core'
|
import { merge, cached } from '../util/core'
|
||||||
import { parseQuery, stringifyQuery } from './util'
|
import { parseQuery, stringifyQuery } from './util'
|
||||||
|
|
||||||
function replaceHash (path) {
|
function replaceHash (path) {
|
||||||
@@ -8,6 +8,11 @@ function replaceHash (path) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const replaceSlug = cached(path => {
|
||||||
|
return path
|
||||||
|
.replace('#', '?id=')
|
||||||
|
.replace(/\?(\w+)=/g, (_, slug) => slug === 'id' ? '?id=' : `&${slug}=`)
|
||||||
|
})
|
||||||
/**
|
/**
|
||||||
* Normalize the current url
|
* Normalize the current url
|
||||||
*
|
*
|
||||||
@@ -18,9 +23,7 @@ function replaceHash (path) {
|
|||||||
export function normalize () {
|
export function normalize () {
|
||||||
let path = getHash()
|
let path = getHash()
|
||||||
|
|
||||||
path = path
|
path = replaceSlug(path)
|
||||||
.replace('#', '?id=')
|
|
||||||
.replace(/\?(\w+)=/g, (_, slug) => slug === 'id' ? '?id=' : `&${slug}=`)
|
|
||||||
|
|
||||||
if (path.charAt(0) === '/') return replaceHash(path)
|
if (path.charAt(0) === '/') return replaceHash(path)
|
||||||
replaceHash('/' + path)
|
replaceHash('/' + path)
|
||||||
@@ -62,7 +65,7 @@ export function parse (path = window.location.href) {
|
|||||||
* @param {object} qs query params
|
* @param {object} qs query params
|
||||||
*/
|
*/
|
||||||
export function toURL (path, params) {
|
export function toURL (path, params) {
|
||||||
const route = parse(path)
|
const route = parse(replaceSlug(path))
|
||||||
|
|
||||||
route.query = merge({}, route.query, params)
|
route.query = merge({}, route.query, params)
|
||||||
path = route.path + stringifyQuery(route.query)
|
path = route.path + stringifyQuery(route.query)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { normalize, parse } from './hash'
|
import { normalize, parse } from './hash'
|
||||||
import { getBasePath, cleanPath } from './util'
|
import { getBasePath, getPath } from './util'
|
||||||
import { on } from '../util/dom'
|
import { on } from '../util/dom'
|
||||||
|
|
||||||
function getAlias (path, alias) {
|
function getAlias (path, alias) {
|
||||||
@@ -15,16 +15,16 @@ function getFileName (path) {
|
|||||||
: `${path}.md`
|
: `${path}.md`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function routeMixin (Docsify) {
|
export function routeMixin (proto) {
|
||||||
Docsify.prototype.route = {}
|
proto.route = {}
|
||||||
Docsify.prototype.$getFile = function (path) {
|
proto.$getFile = function (path) {
|
||||||
const { config } = this
|
const { config } = this
|
||||||
const base = getBasePath(config.basePath)
|
const base = getBasePath(config.basePath)
|
||||||
|
|
||||||
path = getAlias(path, config.alias)
|
path = getAlias(path, config.alias)
|
||||||
path = getFileName(path)
|
path = getFileName(path)
|
||||||
path = path === '/README.md' ? ('/' + config.homepage || path) : path
|
path = path === '/README.md' ? ('/' + config.homepage || path) : path
|
||||||
path = cleanPath(base + path)
|
path = getPath(base, path)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
@@ -39,14 +39,14 @@ export function initRoute (vm) {
|
|||||||
on('hashchange', _ => {
|
on('hashchange', _ => {
|
||||||
normalize()
|
normalize()
|
||||||
vm.route = parse()
|
vm.route = parse()
|
||||||
|
vm._updateRender()
|
||||||
|
|
||||||
if (lastRoute.path === vm.route.path) {
|
if (lastRoute.path === vm.route.path) {
|
||||||
// TODO: goto xxx
|
vm.$resetEvents()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vm._fetchCover()
|
vm.$fetch()
|
||||||
vm._fetch()
|
|
||||||
lastRoute = vm.route
|
lastRoute = vm.route
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { cached } from '../util/core'
|
|||||||
const decode = decodeURIComponent
|
const decode = decodeURIComponent
|
||||||
const encode = encodeURIComponent
|
const encode = encodeURIComponent
|
||||||
|
|
||||||
export const parseQuery = cached(query => {
|
export function parseQuery (query) {
|
||||||
const res = {}
|
const res = {}
|
||||||
|
|
||||||
query = query.trim().replace(/^(\?|#|&)/, '')
|
query = query.trim().replace(/^(\?|#|&)/, '')
|
||||||
@@ -19,7 +19,7 @@ export const parseQuery = cached(query => {
|
|||||||
res[parts[0]] = decode(parts[1])
|
res[parts[0]] = decode(parts[1])
|
||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
})
|
}
|
||||||
|
|
||||||
export function stringifyQuery (obj) {
|
export function stringifyQuery (obj) {
|
||||||
const qs = []
|
const qs = []
|
||||||
@@ -37,6 +37,14 @@ export const getBasePath = cached(base => {
|
|||||||
: cleanPath(window.location.pathname + '/' + base)
|
: cleanPath(window.location.pathname + '/' + base)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export function getPath (...args) {
|
||||||
|
const path = args.find(path => /:|(\/{2})/.test(path))
|
||||||
|
|
||||||
|
if (path) return path
|
||||||
|
|
||||||
|
return cleanPath(args.join('/'))
|
||||||
|
}
|
||||||
|
|
||||||
export const getRoot = cached(path => {
|
export const getRoot = cached(path => {
|
||||||
return /\/$/g.test(path) ? path : path.match(/(\S*\/)[^\/]+$/)[1]
|
return /\/$/g.test(path) ? path : path.match(/(\S*\/)[^\/]+$/)[1]
|
||||||
})
|
})
|
||||||
|
|||||||
145
src/index.js
145
src/index.js
@@ -1,145 +0,0 @@
|
|||||||
import * as utils from './util'
|
|
||||||
import { scrollIntoView, activeLink } from './event'
|
|
||||||
import * as render from './render'
|
|
||||||
import Hook from './hook'
|
|
||||||
|
|
||||||
const OPTIONS = utils.merge({
|
|
||||||
el: '#app',
|
|
||||||
repo: '',
|
|
||||||
maxLevel: 6,
|
|
||||||
subMaxLevel: 0,
|
|
||||||
loadSidebar: null,
|
|
||||||
loadNavbar: null,
|
|
||||||
homepage: 'README.md',
|
|
||||||
coverpage: '',
|
|
||||||
basePath: '',
|
|
||||||
auto2top: false,
|
|
||||||
name: '',
|
|
||||||
themeColor: '',
|
|
||||||
nameLink: window.location.pathname,
|
|
||||||
ga: ''
|
|
||||||
}, window.$docsify)
|
|
||||||
const script = document.currentScript || [].slice.call(document.getElementsByTagName('script')).pop()
|
|
||||||
|
|
||||||
// load configuration for script attribute
|
|
||||||
if (script) {
|
|
||||||
for (const prop in OPTIONS) {
|
|
||||||
const val = script.getAttribute('data-' + utils.camel2kebab(prop))
|
|
||||||
OPTIONS[prop] = utils.isNil(val) ? OPTIONS[prop] : (val || true)
|
|
||||||
}
|
|
||||||
if (OPTIONS.loadSidebar === true) OPTIONS.loadSidebar = '_sidebar.md'
|
|
||||||
if (OPTIONS.loadNavbar === true) OPTIONS.loadNavbar = '_navbar.md'
|
|
||||||
if (OPTIONS.coverpage === true) OPTIONS.coverpage = '_coverpage.md'
|
|
||||||
if (OPTIONS.repo === true) OPTIONS.repo = ''
|
|
||||||
if (OPTIONS.name === true) OPTIONS.name = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const hook = new Hook()
|
|
||||||
|
|
||||||
// utils
|
|
||||||
window.$docsify = OPTIONS
|
|
||||||
window.Docsify = {
|
|
||||||
installed: true,
|
|
||||||
utils: utils.merge({}, utils),
|
|
||||||
hook
|
|
||||||
}
|
|
||||||
|
|
||||||
// load options
|
|
||||||
render.init()
|
|
||||||
|
|
||||||
let cacheRoute = null
|
|
||||||
let cacheXhr = null
|
|
||||||
|
|
||||||
const getAlias = function (route) {
|
|
||||||
if (OPTIONS.alias[route]) {
|
|
||||||
return getAlias(OPTIONS.alias[route])
|
|
||||||
} else {
|
|
||||||
return route
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mainRender = function (cb) {
|
|
||||||
let page
|
|
||||||
let route = utils.getRoute()
|
|
||||||
if (cacheRoute === route) return cb()
|
|
||||||
|
|
||||||
let basePath = cacheRoute = OPTIONS.basePath + route
|
|
||||||
|
|
||||||
if (!/\//.test(basePath)) {
|
|
||||||
basePath = ''
|
|
||||||
} else if (basePath && !/\/$/.test(basePath)) {
|
|
||||||
basePath = basePath.match(/(\S*\/)[^\/]+$/)[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace route
|
|
||||||
if (OPTIONS.alias && OPTIONS.alias['/' + route]) {
|
|
||||||
route = getAlias('/' + route)
|
|
||||||
} else {
|
|
||||||
route = (OPTIONS.basePath + route).replace(/\/+/, '/')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!route) {
|
|
||||||
page = OPTIONS.homepage || 'README.md'
|
|
||||||
} else if (/\.md$/.test(route)) {
|
|
||||||
page = route
|
|
||||||
} else if (/\/$/.test(route)) {
|
|
||||||
page = `${route}README.md`
|
|
||||||
} else {
|
|
||||||
page = `${route}.md`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render Cover page
|
|
||||||
if (OPTIONS.coverpage && page === OPTIONS.homepage) {
|
|
||||||
utils.load(OPTIONS.coverpage).then(render.renderCover)
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheXhr && cacheXhr.abort && cacheXhr.abort()
|
|
||||||
// Render markdown file
|
|
||||||
cacheXhr = utils.load(page, 'GET', render.renderLoading)
|
|
||||||
cacheXhr.then(result => {
|
|
||||||
render.renderArticle(result)
|
|
||||||
// clear cover
|
|
||||||
if (OPTIONS.coverpage && page !== OPTIONS.homepage) render.renderCover()
|
|
||||||
// render sidebar
|
|
||||||
if (OPTIONS.loadSidebar) {
|
|
||||||
const renderSidebar = result => { render.renderSidebar(result); cb() }
|
|
||||||
|
|
||||||
utils.load(basePath + OPTIONS.loadSidebar).then(renderSidebar,
|
|
||||||
_ => utils.load(OPTIONS.loadSidebar).then(renderSidebar))
|
|
||||||
} else {
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
}, _ => render.renderArticle(null))
|
|
||||||
|
|
||||||
// Render navbar
|
|
||||||
if (OPTIONS.loadNavbar) {
|
|
||||||
utils.load(basePath + OPTIONS.loadNavbar).then(render.renderNavbar,
|
|
||||||
_ => utils.load(OPTIONS.loadNavbar).then(render.renderNavbar))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Docsify = function () {
|
|
||||||
setTimeout(_ => {
|
|
||||||
;[].concat(OPTIONS.plugins).forEach(fn => typeof fn === 'function' && fn(hook))
|
|
||||||
window.Docsify.hook.emit('init')
|
|
||||||
|
|
||||||
const dom = document.querySelector(OPTIONS.el) || document.body
|
|
||||||
const replace = dom !== document.body
|
|
||||||
const main = function () {
|
|
||||||
mainRender(_ => {
|
|
||||||
scrollIntoView()
|
|
||||||
activeLink('nav')
|
|
||||||
window.Docsify.hook.emit('doneEach')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render app
|
|
||||||
render.renderApp(dom, replace)
|
|
||||||
main()
|
|
||||||
if (!/^#\//.test(window.location.hash)) window.location.hash = '#/'
|
|
||||||
window.addEventListener('hashchange', main)
|
|
||||||
window.Docsify.hook.emit('ready')
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Docsify()
|
|
||||||
237
src/render.js
237
src/render.js
@@ -1,237 +0,0 @@
|
|||||||
import marked from 'marked'
|
|
||||||
import Prism from 'prismjs'
|
|
||||||
import * as tpl from './tpl'
|
|
||||||
import * as event from './event'
|
|
||||||
import * as polyfill from './polyfill'
|
|
||||||
import { genTree, getRoute, isMobile, slugify, merge, emojify } from './util'
|
|
||||||
|
|
||||||
let markdown = marked
|
|
||||||
let toc = []
|
|
||||||
const CACHE = {}
|
|
||||||
const originTitle = document.title
|
|
||||||
|
|
||||||
const renderTo = function (dom, content) {
|
|
||||||
dom = typeof dom === 'object' ? dom : document.querySelector(dom)
|
|
||||||
dom.innerHTML = content
|
|
||||||
slugify.clear()
|
|
||||||
|
|
||||||
return dom
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* init render
|
|
||||||
*/
|
|
||||||
export function init () {
|
|
||||||
const renderer = new marked.Renderer()
|
|
||||||
/**
|
|
||||||
* render anchor tag
|
|
||||||
* @link https://github.com/chjj/marked#overriding-renderer-methods
|
|
||||||
*/
|
|
||||||
renderer.heading = function (text, level) {
|
|
||||||
const slug = slugify(text)
|
|
||||||
let route = ''
|
|
||||||
|
|
||||||
route = `#/${getRoute()}`
|
|
||||||
toc.push({ level, slug: `${route}#${encodeURIComponent(slug)}`, title: text })
|
|
||||||
|
|
||||||
return `<h${level} id="${slug}"><a href="${route}#${slug}" 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) {
|
|
||||||
if (!/:|(\/{2})/.test(href)) {
|
|
||||||
href = `#/${href}`.replace(/\/+/g, '/')
|
|
||||||
}
|
|
||||||
return `<a href="${href}" title="${title || ''}">${text}</a>`
|
|
||||||
}
|
|
||||||
renderer.paragraph = function (text) {
|
|
||||||
if (/^!>/.test(text)) {
|
|
||||||
return tpl.helper('tip', text)
|
|
||||||
} else if (/^\?>/.test(text)) {
|
|
||||||
return tpl.helper('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}"` : ''
|
|
||||||
|
|
||||||
return `<img src="${url}" alt="${text}"${titleHTML} />`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof $docsify.markdown === 'function') {
|
|
||||||
markdown = $docsify.markdown.call(this, markdown, renderer)
|
|
||||||
} else {
|
|
||||||
if ($docsify.markdown && $docsify.markdown.renderer) {
|
|
||||||
$docsify.markdown.renderer = merge(renderer, $docsify.markdown.renderer)
|
|
||||||
}
|
|
||||||
markdown.setOptions(merge({ renderer }, $docsify.markdown))
|
|
||||||
}
|
|
||||||
|
|
||||||
const md = markdown
|
|
||||||
|
|
||||||
markdown = text => emojify(md(text))
|
|
||||||
|
|
||||||
window.Docsify.utils.marked = text => {
|
|
||||||
const result = markdown(text)
|
|
||||||
toc = []
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* App
|
|
||||||
*/
|
|
||||||
export function renderApp (dom, replace) {
|
|
||||||
const nav = document.querySelector('nav') || document.createElement('nav')
|
|
||||||
const body = document.body
|
|
||||||
const head = document.head
|
|
||||||
|
|
||||||
if (!$docsify.repo) nav.classList.add('no-badge')
|
|
||||||
|
|
||||||
dom[replace ? 'outerHTML' : 'innerHTML'] = tpl.corner($docsify.repo) +
|
|
||||||
($docsify.coverpage ? tpl.cover() : '') +
|
|
||||||
tpl.main()
|
|
||||||
body.insertBefore(nav, body.children[0])
|
|
||||||
|
|
||||||
// theme color
|
|
||||||
if ($docsify.themeColor) {
|
|
||||||
head.innerHTML += tpl.theme($docsify.themeColor)
|
|
||||||
polyfill.cssVars()
|
|
||||||
}
|
|
||||||
|
|
||||||
// render name
|
|
||||||
if ($docsify.name) {
|
|
||||||
const aside = document.querySelector('.sidebar')
|
|
||||||
aside.innerHTML = `<h1><a href="${$docsify.nameLink}">${$docsify.name}</a></h1>` + aside.innerHTML
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind toggle
|
|
||||||
event.bindToggle('button.sidebar-toggle')
|
|
||||||
// bind sticky effect
|
|
||||||
if ($docsify.coverpage) {
|
|
||||||
!isMobile() && window.addEventListener('scroll', event.sticky)
|
|
||||||
} else {
|
|
||||||
body.classList.add('sticky')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* article
|
|
||||||
*/
|
|
||||||
export function renderArticle (content) {
|
|
||||||
const hook = window.Docsify.hook
|
|
||||||
const renderFn = function (data) {
|
|
||||||
renderTo('article', data)
|
|
||||||
if (!$docsify.loadSidebar) renderSidebar()
|
|
||||||
|
|
||||||
if (data && typeof Vue !== 'undefined') {
|
|
||||||
CACHE.vm && CACHE.vm.$destroy()
|
|
||||||
|
|
||||||
const script = [].slice.call(
|
|
||||||
document.body.querySelectorAll('article>script'))
|
|
||||||
.filter(script => !/template/.test(script.type)
|
|
||||||
)[0]
|
|
||||||
const code = script ? script.innerText.trim() : null
|
|
||||||
|
|
||||||
script && script.remove()
|
|
||||||
CACHE.vm = code
|
|
||||||
? new Function(`return ${code}`)()
|
|
||||||
: new Vue({ el: 'main' }) // eslint-disable-line
|
|
||||||
CACHE.vm && CACHE.vm.$nextTick(_ => event.scrollActiveSidebar())
|
|
||||||
}
|
|
||||||
if ($docsify.auto2top) setTimeout(() => event.scroll2Top($docsify.auto2top), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
hook.emit('before', content, result => {
|
|
||||||
const html = result ? markdown(result) : ''
|
|
||||||
|
|
||||||
hook.emit('after', html, result => renderFn(result || 'not found'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* navbar
|
|
||||||
*/
|
|
||||||
export function renderNavbar (content) {
|
|
||||||
if (CACHE.navbar && CACHE.navbar === content) return
|
|
||||||
CACHE.navbar = content
|
|
||||||
|
|
||||||
if (content) renderTo('nav', markdown(content))
|
|
||||||
event.activeLink('nav')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sidebar
|
|
||||||
*/
|
|
||||||
export function renderSidebar (content) {
|
|
||||||
let html
|
|
||||||
|
|
||||||
if (content) {
|
|
||||||
html = markdown(content)
|
|
||||||
// find url tag
|
|
||||||
html = html.match(/<ul[^>]*>([\s\S]+)<\/ul>/g)[0]
|
|
||||||
} else {
|
|
||||||
html = tpl.tree(genTree(toc, $docsify.maxLevel), '<ul>')
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTo('.sidebar-nav', html)
|
|
||||||
|
|
||||||
if (toc[0] && toc[0].level === 1) {
|
|
||||||
document.title = `${toc[0].title}${originTitle ? ' - ' + originTitle : ''}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const target = event.activeLink('.sidebar-nav', true)
|
|
||||||
if (target) renderSubSidebar(target)
|
|
||||||
|
|
||||||
toc = []
|
|
||||||
event.scrollActiveSidebar()
|
|
||||||
}
|
|
||||||
|
|
||||||
export function renderSubSidebar (target) {
|
|
||||||
if (!$docsify.subMaxLevel) return
|
|
||||||
toc[0] && toc[0].level === 1 && toc.shift()
|
|
||||||
target.parentNode.innerHTML += tpl.tree(genTree(toc, $docsify.subMaxLevel), '<ul>')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cover Page
|
|
||||||
*/
|
|
||||||
export function renderCover (content) {
|
|
||||||
renderCover.dom = renderCover.dom || document.querySelector('section.cover')
|
|
||||||
if (!content) {
|
|
||||||
renderCover.dom.classList.remove('show')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
renderCover.dom.classList.add('show')
|
|
||||||
if (renderCover.rendered) return event.sticky()
|
|
||||||
|
|
||||||
// render cover
|
|
||||||
const cacheToc = toc.slice()
|
|
||||||
let html = markdown(content)
|
|
||||||
const match = html.trim().match('<p><img[^s]+src="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$')
|
|
||||||
|
|
||||||
toc = cacheToc.slice()
|
|
||||||
|
|
||||||
// render background
|
|
||||||
if (match) {
|
|
||||||
const coverEl = document.querySelector('section.cover')
|
|
||||||
|
|
||||||
if (match[2] === 'color') {
|
|
||||||
coverEl.style.background = match[1] + (match[3] || '')
|
|
||||||
} else {
|
|
||||||
coverEl.classList.add('has-mask')
|
|
||||||
coverEl.style.backgroundImage = `url(${match[1]})`
|
|
||||||
}
|
|
||||||
html = html.replace(match[0], '')
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTo('.cover-main', html)
|
|
||||||
renderCover.rendered = true
|
|
||||||
|
|
||||||
event.sticky()
|
|
||||||
}
|
|
||||||
@@ -46,6 +46,15 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-sub-sidebar {
|
||||||
|
.section-link {
|
||||||
|
&::before {
|
||||||
|
content: '-';
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* markdown content found on pages */
|
/* markdown content found on pages */
|
||||||
.markdown-section h1,
|
.markdown-section h1,
|
||||||
.markdown-section h2,
|
.markdown-section h2,
|
||||||
|
|||||||
Reference in New Issue
Block a user