Compare commits

..
27 Commits
Author SHA1 Message Date
qingwei.li cb592e7bf0 -> v1.4.3 2017-01-01 12:01:36 +08:00
qingwei.li 3c01d1f82d bump 1.4.3 2017-01-01 12:01:23 +08:00
qingwei.li 00253d3148 Tweak style 2017-01-01 12:01:01 +08:00
qingwei.li d23881ba7c Update version 2016-12-31 22:23:47 +08:00
qingwei.li f0a99c0d1d Update change log 2016-12-31 22:06:43 +08:00
qingwei.li b952a3a08c -> v1.4.2 2016-12-31 22:05:10 +08:00
qingwei.li 2bb8641303 bump 1.4.3 2016-12-31 22:04:54 +08:00
qingwei.li 66d7919fa9 Fix href type, fixed #46 2016-12-31 22:04:14 +08:00
qingwei.li 580767bf78 Clear dev.html 2016-12-31 15:12:28 +08:00
qingwei.li d6b996f4c1 Add changelog 2016-12-31 15:07:02 +08:00
qingwei.li b4e0d54d7b Fix menu toggle style 2016-12-31 14:53:27 +08:00
qingwei.li b15b286c5c -> v1.4.1 2016-12-31 14:16:54 +08:00
qingwei.li 5ab86271c2 bump 1.4.1 2016-12-31 14:16:39 +08:00
qingwei.li 9638eee50d fix generate slug, fixed #45 2016-12-31 14:16:15 +08:00
qingwei.li 375a1dd3e1 -> v1.4.0 2016-12-31 13:47:28 +08:00
qingwei.li 3069ad3928 bump 1.4.0 2016-12-31 13:47:16 +08:00
qingwei.li b287f57c86 Add custom cover background 2016-12-31 13:40:29 +08:00
qingwei.li e932ff66b9 max-sub-level => sub-max-level 2016-12-31 11:50:30 +08:00
nsinaandcinwell.li 7a10e4aecf Readme/homepage cleanup 2016-12-31 10:22:31 +08:00
qingwei.li 41e7ef6863 Add docs 2016-12-30 18:09:13 +08:00
qingwei.li 7ce166501b Add render sub menu 2016-12-30 17:58:39 +08:00
qingwei.li b9aeb4a922 Fix scroll highlight when Vue exist 2016-12-27 23:58:26 +08:00
qingwei.li 9ac8293c0a Update docs 2016-12-26 23:47:26 +08:00
qingwei.li aa3121eca2 Bye 🎅 2016-12-25 20:59:08 +08:00
qingwei.li 0e615abbdc -> v1.3.5 2016-12-25 20:13:21 +08:00
qingwei.li cb2b191ba1 bump 1.3.5 2016-12-25 20:13:11 +08:00
qingwei.li 6adcfffba9 fix vue 2016-12-25 20:12:03 +08:00
23 changed files with 412 additions and 241 deletions
+33
View File
@@ -1,3 +1,36 @@
## 1.4.3
### Bug fixes
- Tweak style.
## 1.4.2
### Bug fixes
- Fix toggle button style.
- Support `mailto`, `tel`, etc. href type
- Fix scroll to top.
## 1.4.1
### Bug fixes
- Fix generate slug.
## 1.4.0 Happly new year 🎉
### Features
- Display TOC in the custom sidebar, `data-sub-max-level`.
- Custom background in coverpage.
### Bug fixes
- Fix scroll highlight when Vue exist.
## 1.3.5
### Bug fixes
- Fix vue
## 1.3.4
### Bug fixes
- Supports [vuep](https://github.com/QingWei-Li/vuep)
## 1.3.3
### Bug fixes
- Fixed cover rendering timing
+1 -1
View File
@@ -4,7 +4,7 @@ var fs = require('fs')
http.createServer(function (req, res) {
serveStatic('.')(req, res, function () {
res.writeHead(200, { 'Content-Type': 'text/html' })
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end(fs.readFileSync('dev.html'))
})
}).listen(3000, '0.0.0.0')
+38 -9
View File
@@ -29,7 +29,7 @@ Create `README.md` as the main page
```
# Title
## balabala
## blabla
```
### Deploy!
@@ -38,7 +38,7 @@ Push and open the **GitHub Pages** feature
## CLI
Easy to setup and preivew a docs.
Easy to setup and preview a docs.
### Install
```shell
@@ -96,7 +96,7 @@ Code in `index.html`
### Cover Page
Through the markdown can generate a cover page. create a `_coverpage.md` and set `data-coverpage` in script tag.
Generate a cover page through with markdown. Create a `_coverpage.md` and set `data-coverpage` in script tag.
```markdown
![logo](_media/icon.svg)
@@ -114,6 +114,23 @@ Through the markdown can generate a cover page. create a `_coverpage.md` and set
[Get Started](#quick-start)
```
#### Custom background
Currently the background of cover page is generated randomly. We can customize the background, just using the syntax to add image.
```markdown
# docsify <small>1.2.0</small>
> xxx
[GitHub](https://github.com/QingWei-Li/docsify/)
[Get Started](#quick-start)
<!-- background image -->
![](_media/bg.png)
<!-- background color -->
![color](#f0f0f0)
```
### Options
@@ -125,7 +142,7 @@ Display the [GitHub Corner](http://tholman.com/github-corners/) widget.
```
#### max-level
Toc level.
TOC level.
```html
<script src="//unpkg.com/docsify" data-max-level="6"></script>
@@ -148,7 +165,7 @@ Sidebar with toggle
#### sidebar
Custom sidebar. if it'set, the TOC will be disabeld. Bind global variables on the `data-sidebar`.
Custom sidebar. If it's set, the TOC will be disabled. Bind global variables on the `data-sidebar`.
![image](https://cloud.githubusercontent.com/assets/7565692/20647425/de5ab1c2-b4ce-11e6-863a-135868f2f9b4.png)
@@ -172,8 +189,8 @@ Custom sidebar. if it'set, the TOC will be disabeld. Bind global variables on th
#### load-sidebar
Load sidebar markdown file. If it is configured, load the current directory `_sidebar.md` by default. If the file isn't exist, sidebar will appear TOC.
** you should add `.nojekyll` into docs folder, to prevent GitHub Pages from ignoring the `_sidebar.md`**
Load sidebar markdown file. If it is configured, load the current directory `_sidebar.md` by default. If the file isn't exist, sidebar will appear as a TOC.
** you should add `.nojekyll` into docs folder to prevent GitHub Pages from ignoring the `_sidebar.md`**
```html
<script src="/lib/docsify.js" data-load-sidebar></script>
@@ -201,9 +218,21 @@ The contents of the file can be:
- [HTML5 History Mode](/history-mode)
```
#### sub-max-level
Display TOC in the custom sidebar. The default value is 0.
```html
<script src="/lib/docsify.js" data-load-sidebar data-max-sub-level="4"></script>
```
![image](https://cloud.githubusercontent.com/assets/7565692/21563209/a8894512-ceba-11e6-80eb-fef00b80625c.png)
#### load-navbar
Load navbar markdown file. If it is configured, load the current directory `_navbar.md` by default.
Load navbar markdown file. If it is configured, load the current directory `_navbar.md` by default.
```html
<script src="/lib/docsify.js" data-load-navbar></script>
@@ -250,7 +279,7 @@ Scroll to the top on changing hash.
#### homepage
`README.md` will be render as homepage for your website in docs folder, but sometimes we want to specify another file as a homepage, or even use the `README.md` in your repo. we can use (need `data-router`):
`README.md` will be rendered as a homepage for your website in the docs folder, but sometimes we want to specify another file as a homepage, or even use the `README.md` in your repo. We can use (need `data-router`):
```html
<script src="/lib/docsify.js" data-homepage="https://raw.githubusercontent.com/QingWei-Li/docsify/master/README.md"></script>
+1 -1
View File
@@ -1,6 +1,6 @@
![logo](_media/icon.svg)
# docsify <small>1.3.3</small>
# docsify <small>1.4.2</small>
> A magical documentation site generator.
-56
View File
@@ -1,56 +0,0 @@
// Fork https://github.com/HermannBjorgvin/SnowJs/blob/master/snow.js
(function(){
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var flakeArray = [];
canvas.style.pointerEvents = "none";
canvas.style.position = "absolute";
canvas.style.top = 0;
canvas.style.left = 0;
canvas.style.width = "100vw";
canvas.style.height = "100vh";
document.body.appendChild(canvas);
function canvasResize(){
canvas.height = canvas.offsetHeight;
canvas.width = canvas.offsetWidth;
}
canvasResize();
window.onresize = function() {
canvasResize();
};
var MyMath = Math;
setInterval(function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
var random = MyMath.random();
var distance = .05 + .95 * random;
flake = {};
flake.x = 1.5 * canvas.width * MyMath.random() - .5 * canvas.width;
flake.y = -9;
flake.velX = 2 * distance * (MyMath.random() / 2 + .5);
flake.velY = (4 + 2 * MyMath.random()) * distance;
flake.radius = MyMath.pow(5 * random, 2) / 5;
flake.update = function() {
var t = this;
t.x += t.velX;
t.y += t.velY;
ctx.beginPath();
ctx.arc(t.x, t.y, t.radius, 0, 2 * MyMath.PI, !1);
ctx.fillStyle = "#FFF";
ctx.fill()
};
flakeArray.push(flake);
for (b = 0; b < flakeArray.length; b++) {
flakeArray[b].y > canvas.height ? flakeArray.splice(b, 1) : flakeArray[b].update()
}
}, 16);
})();
-1
View File
@@ -15,7 +15,6 @@
</nav>
<div id="app"></div>
</body>
<script src="_media/snow.js"></script>
<script
src="//unpkg.com/docsify/lib/docsify.min.js"
data-max-level="4"
+30 -1
View File
@@ -121,6 +121,23 @@ docsify serve docs
```
#### 自定义封面背景
默认的背景是随机生成的,你可以自定义背景色或者背景图片。只需要在文档末尾用添加图片的 Markdown 语法
```markdown
# docsify <small>1.2.0</small>
> xxx
[GitHub](https://github.com/QingWei-Li/docsify/)
[Get Started](#quick-start)
<!-- 背景图片 -->
![](_media/bg.png)
<!-- 背景色 -->
![color](#f0f0f0)
```
### 配置参数
@@ -206,6 +223,18 @@ Sidebar 开关按钮
- [HTML5 History Mode](/history-mode)
```
#### sub-max-level
显示 TOC 在自定义的侧边栏里,默认最大显示 0 层。
```html
<script src="/lib/docsify.js" data-load-sidebar data-max-sub-level="4"></script>
```
![image](https://cloud.githubusercontent.com/assets/7565692/21563209/a8894512-ceba-11e6-80eb-fef00b80625c.png)
#### load-navbar
读取导航配置文件,如果配置,默认加载当前目录下的 `_navbar.md`。如果文件不存在,会显示 html 里定义的导航栏。
@@ -277,7 +306,7 @@ Sidebar 开关按钮
#### coverpage
生成封面,参考 [#封面](#封面).
生成封面,参考 [#封面](/zh-cn#封面).
```html
<script src="/lib/docsify.js" data-coverpage></script>
+115 -69
View File
@@ -114,6 +114,35 @@ function isMobile () {
return document.body.clientWidth <= 600
}
function slugify (string) {
var re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,.\/:;<=>?@\[\]^`{|}~]/g;
var maintainCase = false;
var replacement = '-';
slugify.occurrences = slugify.occurrences || {};
if (typeof string !== 'string') { return '' }
if (!maintainCase) { string = string.toLowerCase(); }
var slug = string.trim()
.replace(re, '')
.replace(/\s/g, replacement);
var occurrences = slugify.occurrences[slug];
if (slugify.occurrences.hasOwnProperty(slug)) {
occurrences++;
} else {
occurrences = 0;
}
slugify.occurrences[slug] = occurrences;
if (occurrences) {
slug = slug + '-' + occurrences;
}
return slug
}
/**
* Active sidebar when scroll
* @link https://buble.surge.sh/
@@ -130,7 +159,10 @@ function scrollActiveSidebar () {
var li = lis[i];
var href = li.querySelector('a').getAttribute('href');
if (href !== '/') { href = href.match(/#([^#]+)$/g)[0].slice(1); }
if (href !== '/') {
var match = href.match('#([^#]+)$');
if (match && match.length) { href = match[0].slice(1); }
}
nav[decodeURIComponent(href)] = li;
}
@@ -174,19 +206,23 @@ function activeLink (dom, activeParent) {
var host = window.location.href;
dom = typeof dom === 'object' ? dom : document.querySelector(dom);
if (!dom) { return
if (!dom) { return }
var target;[].slice.call(dom.querySelectorAll('a'))
.sort(function (a, b) { return b.href.length - a.href.length; })
.forEach(function (node) {
if (host.indexOf(node.href) === 0 && !target) {
activeParent
? node.parentNode.setAttribute('class', 'active')
: node.setAttribute('class', 'active');
target = node;
} else {
activeParent
? node.parentNode.removeAttribute('class')
: node.removeAttribute('class');
}
});
; }[].slice.call(dom.querySelectorAll('a')).forEach(function (node) {
if (node.href === host) {
activeParent
? node.parentNode.setAttribute('class', 'active')
: node.setAttribute('class', 'active');
} else {
activeParent
? node.parentNode.removeAttribute('class')
: node.removeAttribute('class');
}
});
return target
}
/**
@@ -199,19 +235,14 @@ function bindToggle (dom) {
dom.addEventListener('click', function () { return body.classList.toggle('close'); });
if (!/mobile/i.test(navigator.userAgent)) { return }
document.querySelector('aside').addEventListener('click', function (event) {
body.classList.toggle('close');
});
if (isMobile()) {
document.querySelector('aside')
.addEventListener('click', function (_) { return body.classList.toggle('close'); });
}
}
var cacheContentDOM;
function scroll2Top () {
if (!cacheContentDOM) {
var dom = isMobile() ? 'body' : 'section.content';
cacheContentDOM = document.querySelector(dom);
}
cacheContentDOM.scrollTop = 0;
document.body.scrollTop = 0;
}
function sticky () {
@@ -2338,7 +2369,10 @@ function corner (data) {
* Render main content
*/
function main (tpl) {
return ("<main>\n " + tpl + "\n <aside class=\"sidebar\"></aside>\n <section class=\"content\">\n <article class=\"markdown-section\"></article>\n </section>\n </main>")
var aside = tpl + "<aside class=\"sidebar\"></aside>";
return (isMobile() ? (aside + "<main>") : ("<main>" + aside)) +
"<section class=\"content\">\n <article class=\"markdown-section\"></article>\n </section>\n </main>"
}
/**
@@ -2348,11 +2382,11 @@ function cover () {
var SL = ', 100%, 85%';
var bgc = "linear-gradient(to left bottom, hsl(" + (Math.floor(Math.random() * 255) + SL) + ") 0%, hsl(" + (Math.floor(Math.random() * 255) + SL) + ") 100%)";
return ("<section class=\"cover\" style=\"background: " + bgc + "\">\n <div class=\"cover-main\"></div>\n </section>")
return ("<section class=\"cover\" style=\"background: " + bgc + "\">\n <div class=\"cover-main\"></div>\n <div class=\"mask\"></div>\n </section>")
}
function toggle () {
return "<button class=\"sidebar-toggle\">\n <span></span><span></span><span></span>\n </button>"
return "<button class=\"sidebar-toggle\">\n <div class=\"sidebar-toggle-button\">\n <span></span><span></span><span></span>\n </div>\n </button>"
}
/**
@@ -2393,9 +2427,7 @@ var renderer = new marked.Renderer();
* @link https://github.com/chjj/marked#overriding-renderer-methods
*/
renderer.heading = function (text, level) {
var slug = text.toLowerCase()
.replace(/<(?:.|\n)*?>/gm, '')
.replace(/[^\w|\u4e00-\u9fa5]+/g, '-');
var slug = slugify(text);
var route = '';
if (OPTIONS$1.router) {
@@ -2404,18 +2436,19 @@ renderer.heading = function (text, level) {
toc.push({ level: level, slug: (route + "#" + (encodeURIComponent(slug))), title: text });
return ("<a href=\"" + route + "#" + slug + "\" data-id=\"" + slug + "\" class=\"anchor\"><h" + level + " id=\"" + slug + "\">" + text + "</h" + level + "></a>")
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) {
if ( lang === void 0 ) lang = '';
var hl = prism.highlight(code, prism.languages[lang] || prism.languages.markup);
var hl = prism.highlight(code, prism.languages[lang] || prism.languages.markup)
.replace(/{{/g, '<span>{{</span>');
return ("<pre data-lang=\"" + lang + "\"><code class=\"lang-" + lang + "\">" + hl + "</code></pre>")
};
renderer.link = function (href, title, text) {
if (OPTIONS$1.router && !/^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/.test(href)) {
if (OPTIONS$1.router && !/:/.test(href)) {
href = ("#/" + href).replace(/\/\//g, '/');
}
@@ -2451,12 +2484,12 @@ function renderApp (dom, replace) {
*/
function renderArticle (content) {
renderTo('article', content ? marked(content) : 'not found');
if (!renderSidebar.rendered) { renderSidebar(null, OPTIONS$1); }
if (!renderNavbar.rendered) { renderNavbar(null, OPTIONS$1); }
renderSidebar.rendered = false;
renderNavbar.rendered = false;
if (!OPTIONS$1.sidebar && !OPTIONS$1.loadSidebar) { renderSidebar(); }
if (typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') { new Vue({ el: 'main' }); } // eslint-disable-line
if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') {
var vm = new Vue({ el: 'main' }); // eslint-disable-line
vm.$nextTick(function (_) { return scrollActiveSidebar(); });
}
if (OPTIONS$1.auto2top) { scroll2Top(); }
}
@@ -2466,7 +2499,6 @@ function renderArticle (content) {
function renderNavbar (content) {
if (CACHE.navbar && CACHE.navbar === content) { return }
CACHE.navbar = content;
renderNavbar.rendered = true;
if (content) { renderTo('nav', marked(content)); }
activeLink('nav');
@@ -2476,23 +2508,27 @@ function renderNavbar (content) {
* sidebar
*/
function renderSidebar (content) {
var isToc = false;
var html;
if (content) {
content = marked(content);
html = marked(content);
} else if (OPTIONS$1.sidebar) {
content = tree(OPTIONS$1.sidebar, '<ul>');
html = tree(OPTIONS$1.sidebar, '<ul>');
} else {
content = tree(genTree(toc, OPTIONS$1.maxLevel), '<ul>');
isToc = true;
html = tree(genTree(toc, OPTIONS$1.maxLevel), '<ul>');
}
renderSidebar.rendered = true;
if (CACHE.sidebar && CACHE.sidebar === content) { return }
CACHE.sidebar = content;
renderTo('aside.sidebar', content);
if (isToc) { scrollActiveSidebar(); }
renderTo('aside.sidebar', html);
var target = activeLink('aside.sidebar', true);
if (content) { renderSubSidebar(target); }
toc = [];
scrollActiveSidebar();
}
function renderSubSidebar (target) {
if (!OPTIONS$1.subMaxLevel) { return }
target.parentNode.innerHTML += tree(genTree(toc, OPTIONS$1.subMaxLevel), '<ul>');
}
/**
@@ -2501,12 +2537,31 @@ function renderSidebar (content) {
function renderCover (content) {
renderCover.dom = renderCover.dom || document.querySelector('section.cover');
if (!content) {
renderCover.dom.classList.add('hidden');
} else {
renderCover.dom.classList.remove('hidden');
!renderCover.rendered && renderTo('.cover-main', marked(content));
renderCover.rendered = true;
renderCover.dom.classList.remove('show');
return
}
renderCover.dom.classList.add('show');
if (renderCover.rendered) { return }
// render cover
var html = marked(content);
var match = html.trim().match('<p><img[^s]+src="(.*?)"[^a]+alt="(.*?)"></p>$');
// render background
if (match) {
var coverEl = document.querySelector('section.cover');
if (match[2] === 'color') {
coverEl.style.background = match[1];
} else {
coverEl.classList.add('has-mask');
coverEl.style.backgroundImage = "url(" + (match[1]) + ")";
}
html = html.replace(match[0], '');
}
renderTo('.cover-main', html);
renderCover.rendered = true;
sticky();
}
@@ -2560,6 +2615,7 @@ var OPTIONS = {
el: '#app',
repo: '',
maxLevel: 6,
subMaxLevel: 0,
sidebar: '',
sidebarToggle: false,
loadSidebar: null,
@@ -2594,7 +2650,6 @@ var mainRender = function (cb) {
var route = OPTIONS.basePath + getRoute();
if (cacheRoute === route) { return cb() }
var wait;
var basePath = cacheRoute = route;
if (!/\//.test(basePath)) {
@@ -2626,22 +2681,16 @@ var mainRender = function (cb) {
if (OPTIONS.coverpage && page !== OPTIONS.homepage) { renderCover(); }
// render sidebar
if (OPTIONS.loadSidebar) {
if (wait === false) { cb(); }
wait = false;
load(basePath + OPTIONS.loadSidebar)
.then(function (result) {
renderSidebar(result);
cb();
});
} else {
cb();
}
}, function (_) { return renderArticle(null); });
// Render sidebar
if (OPTIONS.loadSidebar) {
load(basePath + OPTIONS.loadSidebar).then(function (result) {
renderSidebar(result);
if (wait === false) { cb(); }
wait = false;
});
}
// Render navbar
if (OPTIONS.loadNavbar) {
load(basePath + OPTIONS.loadNavbar).then(renderNavbar);
@@ -2652,10 +2701,7 @@ var Docsify = function () {
var dom = document.querySelector(OPTIONS.el) || document.body;
var replace = dom !== document.body;
var main = function () {
mainRender(function (_) {
activeLink('aside.sidebar', true);
scrollIntoView();
});
mainRender(function (_) { return scrollIntoView(); });
};
// Render app
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "docsify",
"version": "1.3.4",
"version": "1.4.3",
"description": "A magical documentation generator.",
"main": "lib/docsify.js",
"files": [
+26 -22
View File
@@ -16,7 +16,10 @@ export function scrollActiveSidebar () {
const li = lis[i]
let href = li.querySelector('a').getAttribute('href')
if (href !== '/') href = href.match(/#([^#]+)$/g)[0].slice(1)
if (href !== '/') {
const match = href.match('#([^#]+)$')
if (match && match.length) href = match[0].slice(1)
}
nav[decodeURIComponent(href)] = li
}
@@ -61,18 +64,24 @@ export function activeLink (dom, activeParent) {
dom = typeof dom === 'object' ? dom : document.querySelector(dom)
if (!dom) return
let target
;[].slice.call(dom.querySelectorAll('a')).forEach(node => {
if (node.href === host) {
activeParent
? node.parentNode.setAttribute('class', 'active')
: node.setAttribute('class', 'active')
} else {
activeParent
? node.parentNode.removeAttribute('class')
: node.removeAttribute('class')
}
})
;[].slice.call(dom.querySelectorAll('a'))
.sort((a, b) => b.href.length - a.href.length)
.forEach(node => {
if (host.indexOf(node.href) === 0 && !target) {
activeParent
? node.parentNode.setAttribute('class', 'active')
: node.setAttribute('class', 'active')
target = node
} else {
activeParent
? node.parentNode.removeAttribute('class')
: node.removeAttribute('class')
}
})
return target
}
/**
@@ -85,19 +94,14 @@ export function bindToggle (dom) {
dom.addEventListener('click', () => body.classList.toggle('close'))
if (!/mobile/i.test(navigator.userAgent)) return
document.querySelector('aside').addEventListener('click', event => {
body.classList.toggle('close')
})
if (isMobile()) {
document.querySelector('aside')
.addEventListener('click', _ => body.classList.toggle('close'))
}
}
let cacheContentDOM
export function scroll2Top () {
if (!cacheContentDOM) {
const dom = isMobile() ? 'body' : 'section.content'
cacheContentDOM = document.querySelector(dom)
}
cacheContentDOM.scrollTop = 0
document.body.scrollTop = 0
}
export function sticky () {
+8 -17
View File
@@ -1,11 +1,12 @@
import { load, camel2kebab, isNil, getRoute } from './util'
import { activeLink, scrollIntoView } from './event'
import { scrollIntoView } from './event'
import * as render from './render'
const OPTIONS = {
el: '#app',
repo: '',
maxLevel: 6,
subMaxLevel: 0,
sidebar: '',
sidebarToggle: false,
loadSidebar: null,
@@ -40,7 +41,6 @@ const mainRender = function (cb) {
const route = OPTIONS.basePath + getRoute()
if (cacheRoute === route) return cb()
let wait
let basePath = cacheRoute = route
if (!/\//.test(basePath)) {
@@ -72,22 +72,16 @@ const mainRender = function (cb) {
if (OPTIONS.coverpage && page !== OPTIONS.homepage) render.renderCover()
// render sidebar
if (OPTIONS.loadSidebar) {
if (wait === false) cb()
wait = false
load(basePath + OPTIONS.loadSidebar)
.then(result => {
render.renderSidebar(result)
cb()
})
} else {
cb()
}
}, _ => render.renderArticle(null))
// Render sidebar
if (OPTIONS.loadSidebar) {
load(basePath + OPTIONS.loadSidebar).then(result => {
render.renderSidebar(result)
if (wait === false) cb()
wait = false
})
}
// Render navbar
if (OPTIONS.loadNavbar) {
load(basePath + OPTIONS.loadNavbar).then(render.renderNavbar)
@@ -98,10 +92,7 @@ const Docsify = function () {
const dom = document.querySelector(OPTIONS.el) || document.body
const replace = dom !== document.body
const main = function () {
mainRender(_ => {
activeLink('aside.sidebar', true)
scrollIntoView()
})
mainRender(_ => scrollIntoView())
}
// Render app
+48 -27
View File
@@ -2,7 +2,7 @@ import marked from 'marked'
import Prism from 'prismjs'
import * as tpl from './tpl'
import { activeLink, scrollActiveSidebar, bindToggle, scroll2Top, sticky } from './event'
import { genTree, getRoute, isMobile } from './util'
import { genTree, getRoute, isMobile, slugify } from './util'
let OPTIONS = {}
const CACHE = {}
@@ -21,9 +21,7 @@ const renderer = new marked.Renderer()
* @link https://github.com/chjj/marked#overriding-renderer-methods
*/
renderer.heading = function (text, level) {
const slug = text.toLowerCase()
.replace(/<(?:.|\n)*?>/gm, '')
.replace(/[^\w|\u4e00-\u9fa5]+/g, '-')
const slug = slugify(text)
let route = ''
if (OPTIONS.router) {
@@ -32,16 +30,17 @@ renderer.heading = function (text, level) {
toc.push({ level, slug: `${route}#${encodeURIComponent(slug)}`, title: text })
return `<a href="${route}#${slug}" data-id="${slug}" class="anchor"><h${level} id="${slug}">${text}</h${level}></a>`
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)
.replace(/{{/g, '<span>{{</span>')
return `<pre data-lang="${lang}"><code class="lang-${lang}">${hl}</code></pre>`
}
renderer.link = function (href, title, text) {
if (OPTIONS.router && !/^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/.test(href)) {
if (OPTIONS.router && !/:/.test(href)) {
href = `#/${href}`.replace(/\/\//g, '/')
}
@@ -77,12 +76,12 @@ export function renderApp (dom, replace) {
*/
export function renderArticle (content) {
renderTo('article', content ? marked(content) : 'not found')
if (!renderSidebar.rendered) renderSidebar(null, OPTIONS)
if (!renderNavbar.rendered) renderNavbar(null, OPTIONS)
renderSidebar.rendered = false
renderNavbar.rendered = false
if (!OPTIONS.sidebar && !OPTIONS.loadSidebar) renderSidebar()
if (typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') new Vue({ el: 'main' }) // eslint-disable-line
if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') {
const vm = new Vue({ el: 'main' }) // eslint-disable-line
vm.$nextTick(_ => scrollActiveSidebar())
}
if (OPTIONS.auto2top) scroll2Top()
}
@@ -92,7 +91,6 @@ export function renderArticle (content) {
export function renderNavbar (content) {
if (CACHE.navbar && CACHE.navbar === content) return
CACHE.navbar = content
renderNavbar.rendered = true
if (content) renderTo('nav', marked(content))
activeLink('nav')
@@ -102,23 +100,27 @@ export function renderNavbar (content) {
* sidebar
*/
export function renderSidebar (content) {
let isToc = false
let html
if (content) {
content = marked(content)
html = marked(content)
} else if (OPTIONS.sidebar) {
content = tpl.tree(OPTIONS.sidebar, '<ul>')
html = tpl.tree(OPTIONS.sidebar, '<ul>')
} else {
content = tpl.tree(genTree(toc, OPTIONS.maxLevel), '<ul>')
isToc = true
html = tpl.tree(genTree(toc, OPTIONS.maxLevel), '<ul>')
}
renderSidebar.rendered = true
if (CACHE.sidebar && CACHE.sidebar === content) return
CACHE.sidebar = content
renderTo('aside.sidebar', content)
if (isToc) scrollActiveSidebar()
renderTo('aside.sidebar', html)
const target = activeLink('aside.sidebar', true)
if (content) renderSubSidebar(target)
toc = []
scrollActiveSidebar()
}
export function renderSubSidebar (target) {
if (!OPTIONS.subMaxLevel) return
target.parentNode.innerHTML += tpl.tree(genTree(toc, OPTIONS.subMaxLevel), '<ul>')
}
/**
@@ -127,12 +129,31 @@ export function renderSidebar (content) {
export function renderCover (content) {
renderCover.dom = renderCover.dom || document.querySelector('section.cover')
if (!content) {
renderCover.dom.classList.add('hidden')
} else {
renderCover.dom.classList.remove('hidden')
!renderCover.rendered && renderTo('.cover-main', marked(content))
renderCover.rendered = true
renderCover.dom.classList.remove('show')
return
}
renderCover.dom.classList.add('show')
if (renderCover.rendered) return
// render cover
let html = marked(content)
const match = html.trim().match('<p><img[^s]+src="(.*?)"[^a]+alt="(.*?)"></p>$')
// render background
if (match) {
const coverEl = document.querySelector('section.cover')
if (match[2] === 'color') {
coverEl.style.background = match[1]
} else {
coverEl.classList.add('has-mask')
coverEl.style.backgroundImage = `url(${match[1]})`
}
html = html.replace(match[0], '')
}
renderTo('.cover-main', html)
renderCover.rendered = true
sticky()
}
+20 -5
View File
@@ -1,16 +1,27 @@
section.cover {
height: 100vh;
display: flex;
align-items: center;
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
height: 100vh;
display: none;
&.hidden {
display: none;
&.show {
display: flex;
}
&.has-mask .mask {
position: absolute;
size: 100%;
background-color: $color-bg;
opacity: .8;
}
.cover-main {
flex: 1;
text-align: center;
margin: 0 16px;
margin: -20px 16px 0;
z-index: 1;
}
a {
@@ -35,6 +46,10 @@ section.cover {
font-weight: 300;
color: inherit;
a {
display: block;
}
small {
position: absolute;
bottom: -.4375rem;
+38 -11
View File
@@ -31,6 +31,7 @@ body {
letter-spacing: 0;
margin: 0;
overflow-x: hidden;
color: $color-text;
}
img {
@@ -95,7 +96,7 @@ nav {
display: inline-block;
ul {
background-color: rgba(#fff, .6);
background-color: rgba($color-bg, .6);
border: 1px solid $color-primary;
opacity: 0;
overflow: hidden;
@@ -150,7 +151,7 @@ nav {
}
svg {
color: #fff;
color: $color-bg;
height: 80px;
width: 80px;
fill: $color-primary;
@@ -159,14 +160,18 @@ nav {
/* main */
main {
size: 100% 100%;
size: 100vw 100%;
position: relative;
}
.anchor {
text-decoration: none;
transition: all .3s;
display: block;
display: inline-block;
span {
color: $color-text;
}
&:hover {
text-decoration: underline;
@@ -177,7 +182,7 @@ main {
.sidebar {
border-right: 1px solid rgba(0, 0, 0, .07);
overflow-y: auto;
padding-top: 40px;
padding: 40px 0;
position: absolute 0 * 0 0;
transition: transform 250ms ease-out;
width: $sidebar-width;
@@ -206,17 +211,18 @@ main {
.sidebar-toggle {
background-color: transparent;
border: 0;
bottom: 10px;
left: 10px;
outline: none;
position: absolute;
position: absolute * * 0 0;
text-align: center;
transition: opacity .3s;
width: 30px;
z-index: 30;
outline: none;
width: calc($sidebar-width - 16px);
padding: 10px;
background-color: rgba($color-bg, .8);
&:hover {
.sidebar-toggle-button:hover {
opacity: .4;
}
@@ -290,6 +296,10 @@ body.close {
transform: translateX(-$sidebar-width);
}
.sidebar-toggle {
width: auto;
}
.content {
left: 0;
}
@@ -308,6 +318,11 @@ body.close {
top: 30px;
}
main {
height: auto;
overflow-x: hidden;
}
.sidebar {
left: -$sidebar-width;
transition: transform 250ms ease-out;
@@ -315,7 +330,8 @@ body.close {
.content {
left: 0;
min-width: 100vw;
max-width: 100vw;
position: static;
transition: transform 250ms ease;
}
@@ -323,17 +339,28 @@ body.close {
transition: transform 250ms ease-out;
}
.sidebar-toggle {
width: auto;
background-color: transparent;
}
body.close {
.sidebar {
transform: translateX($sidebar-width);
}
.sidebar-toggle {
width: calc($sidebar-width - 16px);
background-color: rgba($color-bg, .8);
transition: 1s background-color;
}
.content {
transform: translateX($sidebar-width);
}
nav, .github-corner {
transform: translateX($sidebar-width);
display: none;
}
}
+3 -4
View File
@@ -1,18 +1,17 @@
@import url('https://fonts.googleapis.com/css?family=Inconsolata|Inconsolata-Bold');
$color-primary: #0074D9;
$color-bg: #fff;
$color-text: #34495e;
$sidebar-width: 16em;
@import "basic/layout";
@import "basic/coverpage";
body {
color: #34495e;
}
/* sidebar */
.sidebar {
color: #364149;
background-color: #fff;
background-color: $color-bg;
a {
color: #666;
+2
View File
@@ -1,3 +1,5 @@
$color-primary: #000;
$color-bg: #fff;
$color-text: #000;
$sidebar-width: 300px;
@import "basic/layout";
+4 -3
View File
@@ -1,20 +1,21 @@
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600');
$color-primary: #42b983;
$color-bg: #fff;
$color-text: #34495e;
$sidebar-width: 300px;
@import "basic/layout";
@import "basic/coverpage";
body {
background-color: #fff;
color: #34495e;
background-color: $color-bg;
}
/* sidebar */
.sidebar {
color: #364149;
background-color: #fff;
background-color: $color-bg;
li {
margin: 6px 15px;
+11 -8
View File
@@ -1,4 +1,4 @@
import { isMobile } from './util'
/**
* Render github corner
* @param {Object} data
@@ -23,13 +23,13 @@ export function corner (data) {
* Render main content
*/
export function main (tpl) {
return `<main>
${tpl}
<aside class="sidebar"></aside>
<section class="content">
const aside = `${tpl}<aside class="sidebar"></aside>`
return (isMobile() ? `${aside}<main>` : `<main>${aside}`) +
`<section class="content">
<article class="markdown-section"></article>
</section>
</main>`
</main>`
}
/**
@@ -41,13 +41,16 @@ export function cover () {
return `<section class="cover" style="background: ${bgc}">
<div class="cover-main"></div>
<div class="mask"></div>
</section>`
}
export function toggle () {
return `<button class="sidebar-toggle">
<span></span><span></span><span></span>
</button>`
<div class="sidebar-toggle-button">
<span></span><span></span><span></span>
</div>
</button>`
}
/**
+28
View File
@@ -106,3 +106,31 @@ export function isMobile () {
return document.body.clientWidth <= 600
}
export function slugify (string) {
const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,.\/:;<=>?@\[\]^`{|}~]/g
const maintainCase = false
const replacement = '-'
slugify.occurrences = slugify.occurrences || {}
if (typeof string !== 'string') return ''
if (!maintainCase) string = string.toLowerCase()
let slug = string.trim()
.replace(re, '')
.replace(/\s/g, replacement)
let occurrences = slugify.occurrences[slug]
if (slugify.occurrences.hasOwnProperty(slug)) {
occurrences++
} else {
occurrences = 0
}
slugify.occurrences[slug] = occurrences
if (occurrences) {
slug = slug + '-' + occurrences
}
return slug
}