mirror of https://github.com/IoTcat/docsify.git
* Refactor: use iife * Feat: customize sidebar and navbar via markdown file, #6 * Add changelogfix/351
parent
e09b6f218f
commit
35d3bd1647
16 changed files with 608 additions and 316 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,17 +0,0 @@ |
||||
export default function (url, options = {}) { |
||||
const xhr = new XMLHttpRequest() |
||||
|
||||
xhr.open(options.method || 'get', url) |
||||
xhr.send() |
||||
|
||||
return { |
||||
then: function (cb) { |
||||
xhr.addEventListener('load', cb) |
||||
return this |
||||
}, |
||||
catch: function (cb) { |
||||
xhr.addEventListener('error', cb) |
||||
return this |
||||
} |
||||
} |
||||
} |
@ -1,44 +0,0 @@ |
||||
/** |
||||
* @link from https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81
|
||||
*/ |
||||
const tocToTree = function (toc, maxLevel) { |
||||
const headlines = [] |
||||
const last = {} |
||||
|
||||
toc.forEach(headline => { |
||||
const level = headline.level || 1 |
||||
const len = level - 1 |
||||
|
||||
if (level > maxLevel) return |
||||
if (last[len]) { |
||||
last[len].children = last[len].children || [] |
||||
last[len].children.push(headline) |
||||
} else { |
||||
headlines.push(headline) |
||||
} |
||||
last[level] = headline |
||||
}) |
||||
|
||||
return headlines |
||||
} |
||||
|
||||
const buildHeadlinesTree = function (tree, tpl = '') { |
||||
if (!tree || !tree.length) return '' |
||||
|
||||
tree.forEach(node => { |
||||
tpl += `<li><a class="section-link" href="${node.slug}">${node.title}</a></li>` |
||||
if (node.children) { |
||||
tpl += `<li><ul class="children">${buildHeadlinesTree(node.children)}</li></ul>` |
||||
} |
||||
}) |
||||
|
||||
return tpl |
||||
} |
||||
|
||||
export default function (toc, opts) { |
||||
var tree = Array.isArray(opts.sidebar) |
||||
? opts.sidebar |
||||
: tocToTree(toc, opts['max-level']) |
||||
|
||||
return buildHeadlinesTree(tree, '<ul>') |
||||
} |
@ -1,81 +1,52 @@ |
||||
import ajax from './ajax' |
||||
import render from './render' |
||||
import bindEvent from './bind-event' |
||||
import { load, camel2kebab, isNil } from './util' |
||||
import * as render from './render' |
||||
|
||||
const DEFAULT_OPTS = { |
||||
const OPTIONS = { |
||||
el: '#app', |
||||
repo: '', |
||||
'max-level': 6, |
||||
sidebar: '' |
||||
maxLevel: 6, |
||||
sidebar: '', |
||||
loadSidebar: null, |
||||
loadNavbar: null |
||||
} |
||||
|
||||
const script = document.currentScript || [].slice.call(document.getElementsByTagName('script')).pop() |
||||
|
||||
// load configuration for script attribute
|
||||
if (script) { |
||||
for (const prop in DEFAULT_OPTS) { |
||||
DEFAULT_OPTS[prop] = script.getAttribute('data-' + prop) || DEFAULT_OPTS[prop] |
||||
for (const prop in OPTIONS) { |
||||
const val = script.getAttribute('data-' + camel2kebab(prop)) |
||||
OPTIONS[prop] = isNil(val) ? OPTIONS[prop] : true |
||||
} |
||||
if (OPTIONS.loadSidebar === true) OPTIONS.loadSidebar = '_sidebar.md' |
||||
if (OPTIONS.loadNavbar === true) OPTIONS.loadNavbar = '_navbar.md' |
||||
if (OPTIONS.sidebar) OPTIONS.sidebar = window[OPTIONS.sidebar] |
||||
} |
||||
|
||||
class Docsify { |
||||
constructor (opts) { |
||||
Docsify.installed = true |
||||
|
||||
this.opts = Object.assign({}, opts, DEFAULT_OPTS) |
||||
this.replace = true |
||||
this.dom = document.querySelector(this.opts.el) |
||||
if (!this.dom) { |
||||
this.dom = document.body |
||||
this.replace = false |
||||
} |
||||
if (this.opts.sidebar) this.opts.sidebar = window[this.opts.sidebar] |
||||
const Docsify = function () { |
||||
const dom = document.querySelector(OPTIONS.el) || document.body |
||||
const replace = dom !== document.body |
||||
let loc = document.location.pathname |
||||
|
||||
this.loc = document.location.pathname |
||||
if (/\/$/.test(this.loc)) this.loc += 'README' |
||||
if (/\/$/.test(loc)) loc += 'README' |
||||
|
||||
this.load() |
||||
// Render app
|
||||
render.renderApp(dom, replace, OPTIONS) |
||||
|
||||
const nav = document.querySelector('nav') |
||||
if (nav) this.activeNav(nav) |
||||
} |
||||
// Render markdown file
|
||||
load(`${loc}.md`) |
||||
.then(render.renderArticle, _ => render.renderArticle()) |
||||
|
||||
load () { |
||||
ajax(`${this.loc}.md`) |
||||
.then(res => { |
||||
const target = res.target |
||||
if (target.status >= 400) { |
||||
this.render('not found') |
||||
} else { |
||||
this.render(res.target.response) |
||||
bindEvent(!!this.opts.sidebar) |
||||
if (this.opts.sidebar) { |
||||
this.activeNav(document.querySelector('aside.sidebar'), true) |
||||
} |
||||
} |
||||
}) |
||||
.catch(_ => this.render('not found')) |
||||
// Render sidebar
|
||||
if (OPTIONS.loadSidebar) { |
||||
load(OPTIONS.loadSidebar) |
||||
.then(content => render.renderSidebar(content, OPTIONS)) |
||||
} |
||||
|
||||
render (content) { |
||||
this.dom[this.replace ? 'outerHTML' : 'innerHTML'] = render(content, this.opts) |
||||
} |
||||
|
||||
activeNav (elm, activeParentNode) { |
||||
const host = document.location.origin + document.location.pathname |
||||
|
||||
;[].slice.call(elm.querySelectorAll('a')).forEach(node => { |
||||
if (node.href === host) { |
||||
activeParentNode |
||||
? node.parentNode.setAttribute('class', 'active') |
||||
: node.setAttribute('class', 'active') |
||||
} |
||||
}) |
||||
// Render navbar
|
||||
if (OPTIONS.loadNavbar) { |
||||
load(OPTIONS.loadNavbar) |
||||
.then(content => render.renderNavbar(content, OPTIONS)) |
||||
} |
||||
} |
||||
|
||||
window.addEventListener('load', () => { |
||||
if (Docsify.installed) return |
||||
new Docsify() |
||||
}) |
||||
|
||||
export default Docsify |
||||
export default Docsify() |
||||
|
@ -0,0 +1,51 @@ |
||||
|
||||
/** |
||||
* Render github corner |
||||
* @param {Object} data |
||||
* @return {String} |
||||
*/ |
||||
export function corner (data) { |
||||
if (!data) return '' |
||||
if (!/\/\//.test(data)) data = 'https://github.com/' + data |
||||
|
||||
return ` |
||||
<a href="${data}" class="github-corner" aria-label="View source on Github"> |
||||
<svg viewBox="0 0 250 250" aria-hidden="true"> |
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path> |
||||
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path> |
||||
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path> |
||||
</svg> |
||||
</a>` |
||||
} |
||||
|
||||
/** |
||||
* Render main content |
||||
* @return {[type]} [description] |
||||
*/ |
||||
export function main () { |
||||
return `<main>
|
||||
<aside class="sidebar"></aside> |
||||
<section class="content"> |
||||
<article class="markdown-section"></article> |
||||
</section> |
||||
</main>` |
||||
} |
||||
|
||||
/** |
||||
* Render tree |
||||
* @param {Array} tree |
||||
* @param {String} tpl |
||||
* @return {String} |
||||
*/ |
||||
export function tree (toc, tpl = '') { |
||||
if (!toc || !toc.length) return '' |
||||
|
||||
toc.forEach(node => { |
||||
tpl += `<li><a class="section-link" href="${node.slug}">${node.title}</a></li>` |
||||
if (node.children) { |
||||
tpl += `<li><ul class="children">${tree(node.children)}</li></ul>` |
||||
} |
||||
}) |
||||
|
||||
return tpl |
||||
} |
@ -0,0 +1,68 @@ |
||||
/** |
||||
* Simple ajax |
||||
* @param {String} url |
||||
* @param {String} [method=get] |
||||
* @return {Promise} |
||||
*/ |
||||
export function load (url, method = 'get') { |
||||
const xhr = new XMLHttpRequest() |
||||
|
||||
xhr.open(method, url) |
||||
xhr.send() |
||||
|
||||
return { |
||||
then: function (success, error = function () {}) { |
||||
xhr.addEventListener('error', error) |
||||
xhr.addEventListener('load', ({ target }) => { |
||||
target.status >= 400 ? error(target) : success(target.response) |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* gen toc tree |
||||
* @link https://github.com/killercup/grock/blob/5280ae63e16c5739e9233d9009bc235ed7d79a50/styles/solarized/assets/js/behavior.coffee#L54-L81
|
||||
* @param {Array} toc |
||||
* @param {Number} maxLevel |
||||
* @return {Array} |
||||
*/ |
||||
export function genTree (toc, maxLevel) { |
||||
const headlines = [] |
||||
const last = {} |
||||
|
||||
toc.forEach(headline => { |
||||
const level = headline.level || 1 |
||||
const len = level - 1 |
||||
|
||||
if (level > maxLevel) return |
||||
if (last[len]) { |
||||
last[len].children = last[len].children || [] |
||||
last[len].children.push(headline) |
||||
} else { |
||||
headlines.push(headline) |
||||
} |
||||
last[level] = headline |
||||
}) |
||||
|
||||
return headlines |
||||
} |
||||
|
||||
/** |
||||
* camel to kebab |
||||
* @link https://github.com/bokuweb/kebab2camel/blob/master/index.js
|
||||
* @param {String} str |
||||
* @return {String} |
||||
*/ |
||||
export function camel2kebab (str) { |
||||
return str.replace(/([A-Z])/g, m => '-' + m.toLowerCase()) |
||||
} |
||||
|
||||
/** |
||||
* is nil |
||||
* @param {Object} object |
||||
* @return {Boolean} |
||||
*/ |
||||
export function isNil (o) { |
||||
return o === null || o === undefined |
||||
} |
Loading…
Reference in new issue