mirror of
https://github.com/docsifyjs/docsify.git
synced 2025-12-08 19:55:52 +00:00
refactor(router): dupports multiple mode
This commit is contained in:
parent
2a21c40f17
commit
2c7041c8fb
@ -23,7 +23,7 @@
|
||||
"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": "node app.js & nodemon -w src -e js,css --exec 'npm run dev:build'",
|
||||
"test": "eslint src"
|
||||
"test": "eslint src --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"marked": "^0.3.6",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { isMobile } from '../util/env'
|
||||
import * as dom from '../util/dom'
|
||||
import { parse } from '../route/hash'
|
||||
import { parse } from '../router/hash'
|
||||
|
||||
const nav = {}
|
||||
let hoverOver = false
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { isMobile } from '../util/env'
|
||||
import * as dom from '../util/dom'
|
||||
import { getHash } from '../route/hash'
|
||||
import { getHash } from '../router/hash'
|
||||
|
||||
const title = dom.$.title
|
||||
/**
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { get } from './ajax'
|
||||
import { callHook } from '../init/lifecycle'
|
||||
import { getParentPath } from '../route/util'
|
||||
import { getParentPath } from '../router/util'
|
||||
import { noop } from '../util/core'
|
||||
|
||||
function loadNested (path, file, next, vm, first) {
|
||||
@ -9,7 +9,7 @@ function loadNested (path, file, next, vm, first) {
|
||||
|
||||
if (!path) return
|
||||
|
||||
get(vm.$getFile(path + file))
|
||||
get(vm.router.getFile(path + file))
|
||||
.then(next, _ => loadNested(path, file, next, vm))
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ export function fetchMixin (proto) {
|
||||
// Abort last request
|
||||
last && last.abort && last.abort()
|
||||
|
||||
last = get(this.$getFile(path), true)
|
||||
last = get(this.router.getFile(path), true)
|
||||
|
||||
// Current page is html
|
||||
this.isHTML = /\.html$/g.test(path)
|
||||
@ -47,7 +47,7 @@ export function fetchMixin (proto) {
|
||||
proto._fetchCover = function () {
|
||||
const { coverpage } = this.config
|
||||
const root = getParentPath(this.route.path)
|
||||
const path = this.$getFile(root + coverpage)
|
||||
const path = this.router.getFile(root + coverpage)
|
||||
|
||||
if (this.route.path !== '/' || !coverpage) {
|
||||
this._renderCover()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import * as util from './util'
|
||||
import * as dom from './util/dom'
|
||||
import * as render from './render/compiler'
|
||||
import * as route from './route/hash'
|
||||
import * as route from './router/hash'
|
||||
import { slugify } from './render/slugify'
|
||||
import { get } from './fetch/ajax'
|
||||
import marked from 'marked'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { initMixin } from './init'
|
||||
import { routeMixin } from './route'
|
||||
import { routerMixin } from './router'
|
||||
import { renderMixin } from './render'
|
||||
import { fetchMixin } from './fetch'
|
||||
import { eventMixin } from './event'
|
||||
@ -12,7 +12,7 @@ function Docsify () {
|
||||
const proto = Docsify.prototype
|
||||
|
||||
initMixin(proto)
|
||||
routeMixin(proto)
|
||||
routerMixin(proto)
|
||||
renderMixin(proto)
|
||||
fetchMixin(proto)
|
||||
eventMixin(proto)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import config from '../config'
|
||||
import { initLifecycle, callHook } from './lifecycle'
|
||||
import { initRender } from '../render'
|
||||
import { initRoute } from '../route'
|
||||
import { initRouter } from '../router'
|
||||
import { initEvent } from '../event'
|
||||
import { initFetch } from '../fetch'
|
||||
import { isFn } from '../util/core'
|
||||
@ -14,9 +14,9 @@ export function initMixin (proto) {
|
||||
initLifecycle(vm) // Init hooks
|
||||
initPlugin(vm) // Install plugins
|
||||
callHook(vm, 'init')
|
||||
initRouter(vm) // Add router
|
||||
initRender(vm) // Render base DOM
|
||||
initEvent(vm) // Bind events
|
||||
initRoute(vm) // Add hashchange eventListener
|
||||
initFetch(vm) // Fetch data
|
||||
callHook(vm, 'mounted')
|
||||
}
|
||||
|
||||
@ -4,160 +4,165 @@ import { helper as helperTpl, tree as treeTpl } from './tpl'
|
||||
import { genTree } from './gen-tree'
|
||||
import { slugify } from './slugify'
|
||||
import { emojify } from './emojify'
|
||||
import { toURL, parse } from '../route/hash'
|
||||
import { getBasePath, isAbsolutePath, getPath } from '../route/util'
|
||||
import { getBasePath, isAbsolutePath, getPath } from '../router/util'
|
||||
import { isFn, merge, cached } from '../util/core'
|
||||
|
||||
let markdownCompiler = marked
|
||||
let contentBase = ''
|
||||
let currentPath = ''
|
||||
let linkTarget = '_blank'
|
||||
let renderer = new marked.Renderer()
|
||||
const cacheTree = {}
|
||||
let toc = []
|
||||
export class Compiler {
|
||||
constructor (config, router) {
|
||||
this.config = config
|
||||
this.router = router
|
||||
this.cacheTree = {}
|
||||
this.toc = []
|
||||
this.linkTarget = config.externalLinkTarget || '_blank'
|
||||
this.contentBase = getBasePath(config.base)
|
||||
|
||||
/**
|
||||
* Compile markdown content
|
||||
*/
|
||||
export const markdown = cached(text => {
|
||||
let html = ''
|
||||
const renderer = this._initRenderer()
|
||||
let runner
|
||||
const mdConf = config.markdown || {}
|
||||
|
||||
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)
|
||||
html = emojify(html)
|
||||
slugify.clear()
|
||||
this.runner = cached(text => {
|
||||
let html = ''
|
||||
|
||||
return html
|
||||
})
|
||||
if (!text) return text
|
||||
|
||||
markdown.renderer = renderer
|
||||
html = runner(text)
|
||||
html = emojify(html)
|
||||
slugify.clear()
|
||||
|
||||
markdown.init = function (config = {}, {
|
||||
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
|
||||
return html
|
||||
})
|
||||
}
|
||||
|
||||
if (/{docsify-ignore-all}/g.test(text)) {
|
||||
text = text.replace('{docsify-ignore-all}', '')
|
||||
nextToc.title = text
|
||||
nextToc.ignoreAllSubs = true
|
||||
_initRenderer () {
|
||||
const renderer = new marked.Renderer()
|
||||
const { linkTarget, router, toc } = this
|
||||
/**
|
||||
* 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 })
|
||||
nextToc.slug = url
|
||||
toc.push(nextToc)
|
||||
/**
|
||||
* Compile sidebar
|
||||
*/
|
||||
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}>`
|
||||
}
|
||||
// highlight code
|
||||
renderer.code = function (code, lang = '') {
|
||||
const hl = Prism.highlight(code, Prism.languages[lang] || Prism.languages.markup)
|
||||
if (text) {
|
||||
html = this.runner(text)
|
||||
html = html.match(/<ul[^>]*>([\s\S]+)<\/ul>/g)[0]
|
||||
} else {
|
||||
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>`
|
||||
}
|
||||
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 html
|
||||
}
|
||||
|
||||
return `<img src="${url}" data-origin="${href}" alt="${text}"${titleHTML}>`
|
||||
}
|
||||
/**
|
||||
* Compile sub sidebar
|
||||
*/
|
||||
subSidebar (level) {
|
||||
const currentPath = this.router.getCurrentPath()
|
||||
const { cacheTree, toc } = this
|
||||
|
||||
/**
|
||||
* 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].ignoreAllSubs && (this.toc = [])
|
||||
toc[0] && toc[0].level === 1 && toc.shift()
|
||||
toc.forEach((node, i) => {
|
||||
node.ignoreSubHeading && toc.splice(i, 1)
|
||||
})
|
||||
|
||||
const tree = cacheTree[currentPath] || genTree(toc, level)
|
||||
el.parentNode.innerHTML += treeTpl(tree, '<ul class="app-sub-sidebar">')
|
||||
|
||||
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 cssVars from '../util/polyfill/css-vars'
|
||||
import * as tpl from './tpl'
|
||||
import { markdown, sidebar, subSidebar, cover } from './compiler'
|
||||
import { Compiler } from './compiler'
|
||||
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 { isMobile } from '../util/env'
|
||||
import tinydate from 'tinydate'
|
||||
@ -85,32 +85,34 @@ export function renderMixin (proto) {
|
||||
proto._renderSidebar = function (text) {
|
||||
const { maxLevel, subMaxLevel, autoHeader, loadSidebar } = this.config
|
||||
|
||||
this._renderTo('.sidebar-nav', sidebar(text, maxLevel))
|
||||
const active = getAndActive('.sidebar-nav', true, true)
|
||||
subSidebar(loadSidebar ? active : '', subMaxLevel)
|
||||
this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel))
|
||||
const activeEl = getAndActive('.sidebar-nav', true, true)
|
||||
if (loadSidebar && activeEl) {
|
||||
activeEl.parentNode.innerHTML += this.compiler.subSidebar(subMaxLevel)
|
||||
}
|
||||
// bind event
|
||||
this.activeLink = active
|
||||
this.activeLink = activeEl
|
||||
scrollActiveSidebar()
|
||||
|
||||
if (autoHeader && active) {
|
||||
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 = active.innerText
|
||||
h1.innerText = activeEl.innerText
|
||||
dom.before(main, h1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proto._renderNav = function (text) {
|
||||
text && this._renderTo('nav', markdown(text))
|
||||
text && this._renderTo('nav', this.compiler.runner(text))
|
||||
getAndActive('nav')
|
||||
}
|
||||
|
||||
proto._renderMain = function (text, opt = {}) {
|
||||
callHook(this, 'beforeEach', text, result => {
|
||||
let html = this.isHTML ? result : markdown(result)
|
||||
let html = this.isHTML ? result : this.compiler.runner(result)
|
||||
if (opt.updatedAt) {
|
||||
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated)
|
||||
}
|
||||
@ -127,7 +129,7 @@ export function renderMixin (proto) {
|
||||
}
|
||||
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>$')
|
||||
|
||||
if (m) {
|
||||
@ -152,7 +154,6 @@ export function renderMixin (proto) {
|
||||
}
|
||||
|
||||
proto._updateRender = function () {
|
||||
markdown.update()
|
||||
// render name link
|
||||
renderNameLink(this)
|
||||
}
|
||||
@ -162,7 +163,7 @@ export function initRender (vm) {
|
||||
const config = vm.config
|
||||
|
||||
// Init markdown compiler
|
||||
markdown.init(config.markdown, config)
|
||||
vm.compiler = new Compiler(config, vm.router)
|
||||
|
||||
const id = config.el || '#app'
|
||||
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 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) {
|
||||
if (!window.ga) {
|
||||
let ga = window.ga
|
||||
|
||||
if (!ga) {
|
||||
appendScript()
|
||||
window.ga = window.ga || function () {
|
||||
(window.ga.q = window.ga.q || []).push(arguments)
|
||||
ga = ga || function () {
|
||||
(ga.q = ga.q || []).push(arguments)
|
||||
}
|
||||
window.ga.l = Number(new Date())
|
||||
window.ga('create', id, 'auto')
|
||||
ga.l = Number(new Date())
|
||||
ga('create', id, 'auto')
|
||||
}
|
||||
return ga
|
||||
}
|
||||
|
||||
function collect () {
|
||||
init(window.$docsify.ga)
|
||||
window.ga('set', 'page', location.hash)
|
||||
window.ga('send', 'pageview')
|
||||
const ga = init($docsify.ga)
|
||||
|
||||
ga('set', 'page', location.hash)
|
||||
ga('send', 'pageview')
|
||||
}
|
||||
|
||||
const install = function (hook) {
|
||||
if (!window.$docsify.ga) {
|
||||
if (!$docsify.ga) {
|
||||
console.error('[Docsify] ga is required.')
|
||||
return
|
||||
}
|
||||
@ -32,4 +36,4 @@ const install = function (hook) {
|
||||
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++
|
||||
|
||||
helper
|
||||
.get(vm.$getFile(path))
|
||||
.get(vm.router.getFile(path))
|
||||
.then(result => {
|
||||
INDEXS[path] = genIndex(path, result)
|
||||
len === ++count && saveData(config.maxAge)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user