From f095eb888f59c6a1210ba563acbe1fd6cb73be34 Mon Sep 17 00:00:00 2001 From: "qingwei.li" Date: Mon, 29 May 2017 21:32:03 +0800 Subject: [PATCH] feat: support history mode --- src/core/render/compiler.js | 8 ++-- src/core/router/history/base.js | 8 ++-- src/core/router/history/html5.js | 75 ++++++++++++++++++++++++++++++++ src/core/util/env.js | 21 ++++----- 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index 914d4793e..b6000bef5 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -14,7 +14,7 @@ export class Compiler { this.cacheTree = {} this.toc = [] this.linkTarget = config.externalLinkTarget || '_blank' - this.contentBase = getBasePath(config.base) + this.contentBase = getBasePath(config.basePath) const renderer = this._initRenderer() let compile @@ -44,7 +44,7 @@ export class Compiler { _initRenderer () { const renderer = new marked.Renderer() - const { linkTarget, router, toc } = this + const { linkTarget, router, toc, contentBase } = this /** * render anchor tag * @link https://github.com/chjj/marked#overriding-renderer-methods @@ -102,7 +102,7 @@ export class Compiler { const titleHTML = title ? ` title="${title}"` : '' if (!isAbsolutePath(href)) { - url = getPath(this.contentBase, href) + url = getPath(contentBase, href) } return `${text}` @@ -120,7 +120,7 @@ export class Compiler { if (text) { html = this.compile(text) - html = html.match(/]*>([\s\S]+)<\/ul>/g)[0] + html = html && html.match(/]*>([\s\S]+)<\/ul>/g)[0] } else { const tree = this.cacheTree[currentPath] || genTree(this.toc, level) html = treeTpl(tree, '
    ') diff --git a/src/core/router/history/base.js b/src/core/router/history/base.js index 6f68c563f..ff798666b 100644 --- a/src/core/router/history/base.js +++ b/src/core/router/history/base.js @@ -18,10 +18,6 @@ export class History { this.config = config } - onchange (cb = noop) { - cb() - } - getFile (path) { const { config } = this const base = getBasePath(config.basePath) @@ -34,6 +30,10 @@ export class History { return path } + onchange (cb = noop) { + cb() + } + getCurrentPath () {} normalize () {} diff --git a/src/core/router/history/html5.js b/src/core/router/history/html5.js index b1818a37b..9b7c1ab17 100644 --- a/src/core/router/history/html5.js +++ b/src/core/router/history/html5.js @@ -1,8 +1,83 @@ import { History } from './base' +import { merge, noop } from '../../util/core' +import { on } from '../../util/dom' +import { parseQuery, stringifyQuery, cleanPath } from '../util' export class HTML5History extends History { constructor (config) { super(config) this.mode = 'history' } + + getCurrentPath () { + const base = this.config.base + let path = window.location.pathname + + if (base && path.indexOf(base) === 0) { + path = path.slice(base.length) + } + + return (path || '/') + window.location.search + window.location.hash + } + + onchange (cb = noop) { + on('click', e => { + const el = e.target.tagName === 'A' + ? e.target + : e.target.parentNode + + if (el.tagName === 'A' && !/_blank/.test(el.target)) { + e.preventDefault() + const url = el.href + window.history.pushState({ key: url }, '', url) + cb() + } + }) + + on('popstate', cb) + } + + normalize () { + let path = this.getCurrentPath() + + path = path.replace('#', '?id=') + window.history.pushState({ key: path }, '', path) + + return path + } + + /** + * Parse the url + * @param {string} [path=location.href] + * @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 baseIndex = path.indexOf(location.origin) + if (baseIndex > -1) { + path = path.slice(baseIndex + location.origin.length) + } + + return { path, query: parseQuery(query) } + } + + toURL (path, params, currentRoute) { + const local = currentRoute && path[0] === '#' + const route = this.parse(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) + } } diff --git a/src/core/util/env.js b/src/core/util/env.js index 11dfbf305..5d143c95a 100644 --- a/src/core/util/env.js +++ b/src/core/util/env.js @@ -6,17 +6,14 @@ export const isMobile = document.body.clientWidth <= 600 export const inBrowser = typeof window !== 'undefined' +/** + * @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js + */ 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 + // Borrowed wholesale from https://github.com/defunkt/jquery-pjax + return window.history && + window.history.pushState && + window.history.replaceState && + // pushState isn’t reliable on iOS until 5. + !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/) })()