Compare commits

...
105 Commits
Author SHA1 Message Date
qingwei.li aedf16e433 -> v2.2.1 2017-02-11 09:39:44 +08:00
qingwei.li 41e4cb1b04 bump: 2.2.1 2017-02-11 09:39:31 +08:00
qingwei.li 9d3cc89aa9 fix(search): crash when not content, fixed #68 2017-02-11 09:36:48 +08:00
qingwei.li 50f5fc2b22 fix(event): scroll active sidebar 2017-02-11 09:36:48 +08:00
qingwei.li 3941304fda fix(search): not work in mobile 2017-02-11 09:36:48 +08:00
D.Yangandcinwell.li be6a97af92 add showcase of Mybatis-Plus (#69)
add a showcase
2017-02-10 16:56:05 +08:00
qingwei.li 55323a38b8 docs(changelog): update 2017-02-09 22:18:55 +08:00
qingwei.li 5b25e7daca -> v2.2.0 2017-02-09 22:17:29 +08:00
qingwei.li a4bd5fd295 bump: 2.2.0 2017-02-09 22:17:23 +08:00
cinwell.liandGitHub ac61bb0e75 feat(plugins): add Google Analytics plugin (#66) 2017-02-09 22:16:15 +08:00
qingwei.li 9605c18fff -> v2.1.0 2017-02-09 21:26:57 +08:00
qingwei.li dc35b051d3 bump: 2.1.0 2017-02-09 21:26:49 +08:00
qingwei.li 0ca1ece188 docs(README): fix typo 2017-02-09 21:24:14 +08:00
qingwei.li fc140ef957 fix(vue.css): update sidebar style 2017-02-09 21:24:14 +08:00
qingwei.li e1da2f8a84 docs(README): add search 2017-02-09 21:24:14 +08:00
qingwei.li eb5ff3e987 feat: add search, close #43 2017-02-09 21:24:14 +08:00
qingwei.li 12e2479014 fix: render name 2017-02-09 21:24:14 +08:00
qingwei.li 168d8475d5 -> v2.0.3 2017-02-07 20:18:28 +08:00
qingwei.li 7ba434e61d bump: 2.0.3 2017-02-07 20:18:25 +08:00
qingwei.li 8cd386ae49 fix: css var polyfill 2017-02-07 20:17:54 +08:00
qingwei.li 8c7e4d7866 fix: rendering emojis 2017-02-07 20:17:12 +08:00
qingwei.li cbaee21ded fix: css var polyfill 2017-02-07 19:54:09 +08:00
qingwei.li 543cbbce81 -> v2.0.2 2017-02-05 14:50:09 +08:00
qingwei.li 72c34152d4 bump: 2.0.2 2017-02-05 14:50:04 +08:00
qingwei.li 44708554f7 fix: button style in cover page 2017-02-05 14:48:57 +08:00
qingwei.li 6e35f6481a docs: update api 2017-02-05 13:53:36 +08:00
qingwei.li 115c80b2f2 -> v2.0.1 2017-02-05 13:50:36 +08:00
qingwei.li addb34d38a bump: 2.0.1 2017-02-05 13:47:32 +08:00
qingwei.li 1f12009a98 fix border style. 2017-02-05 13:47:03 +08:00
qingwei.li 4abcb02824 -> v2.0.0 2017-02-05 10:27:48 +08:00
qingwei.li d45e325d54 bump 2.0 2017-02-05 10:27:42 +08:00
qingwei.li f2f37eec46 Update version number 2017-02-05 10:26:25 +08:00
qingwei.li fef969f55d clean api 2017-02-05 10:26:25 +08:00
qingwei.li 5cc9f0514c feat: customize the theme color 2017-02-05 10:26:25 +08:00
qingwei.li ae1ad735d5 -> v1.10.5 2017-01-28 11:12:26 +08:00
qingwei.li a35a336d00 bump 1.10.5 2017-01-28 11:12:22 +08:00
qingwei.li 3ec7c89bbb fix initialize the Vue instance 2017-01-28 11:11:58 +08:00
qingwei.li 89f0b1fcbd -> v1.10.4 2017-01-27 15:14:23 +08:00
qingwei.li 6f5bb1a527 bump 1.10.4 2017-01-27 15:14:15 +08:00
qingwei.li 98ec020c7a fix execute script 2017-01-27 15:13:51 +08:00
qingwei.li bb13ddd5df -> v1.10.3 2017-01-27 12:45:42 +08:00
qingwei.li e69efb8954 bump 1.10.3 2017-01-27 12:45:16 +08:00
qingwei.li 0e5f28cf60 Add change log 2017-01-27 12:44:54 +08:00
qingwei.li 7db1f1034a Fix sidebar scroll, fixed #63 2017-01-27 12:44:05 +08:00
qingwei.li b0320416a5 compatible vuep, fixed QingWei-Li/vuep/issues/2 2017-01-27 12:01:36 +08:00
qingwei.li 4a77b8a7f3 -> v1.10.2 2017-01-25 21:39:42 +08:00
qingwei.li 00b13c5e86 bump 1.10.2 2017-01-25 21:39:32 +08:00
qingwei.li 82b7b8226d Fix render emojis again 2017-01-25 21:39:12 +08:00
qingwei.li c9739e71af -> v1.10.1 2017-01-25 21:17:54 +08:00
qingwei.li bda1b76ba5 bump 1.10.1 2017-01-25 21:17:43 +08:00
qingwei.li 11f77cd434 Fix render emojis 2017-01-25 21:16:53 +08:00
qingwei.li 6d42c6b04a -> v1.10.0 2017-01-25 15:08:40 +08:00
qingwei.li 46967e73bf bump 1.10.0 2017-01-25 15:08:28 +08:00
qingwei.li ccdc3c855a Support emoji, closed #61 2017-01-25 15:07:36 +08:00
qingwei.li 7c4b0b7c74 -> v1.9.0 2017-01-24 22:31:44 +08:00
qingwei.li 98d1b19bea bump 1.9.0 2017-01-24 22:31:27 +08:00
cinwell.liandGitHub 0709239c77 1.9 (#62)
* Destroys the vue instance when the route is changed

* Add new doc helper

* Update docs
2017-01-24 22:29:50 +08:00
qingwei.li 73cc43c6bd -> v1.8.0 2017-01-24 11:28:15 +08:00
qingwei.li 9d096b03a4 bump 1.8.0 2017-01-24 11:28:04 +08:00
qingwei.li 2af035ff45 Fix fallback sidebar 2017-01-24 10:18:58 +08:00
qingwei.li 3979f5cd3f Update change log 2017-01-24 10:07:14 +08:00
qingwei.li 9abae495ac Execute script when vue exists. 2017-01-24 10:06:38 +08:00
qingwei.li d179ade7df Using v-pre 2017-01-18 16:35:19 +08:00
qingwei.li d79c7655cd Update license 2017-01-18 16:34:05 +08:00
citycideandcinwell.li af5d2934e4 add Trilogy to showcase list (#58) 2017-01-17 08:05:12 +08:00
Antony Jonesandcinwell.li 129bd4d3a0 Add vudash to list of projects using docsify (#57) 2017-01-15 18:45:32 +08:00
qingwei.li abaf5c0849 -> v1.7.4 2017-01-13 23:33:20 +08:00
qingwei.li 336efb30cb bump 1.7.4 2017-01-13 23:33:04 +08:00
qingwei.li fb8bf89d73 Fix bugs caused by the previous version, fixed #56 2017-01-13 23:32:38 +08:00
qingwei.li 8630591bd2 -> v1.7.3 2017-01-13 23:06:31 +08:00
qingwei.li c47ff83073 bump 1.7.3 2017-01-13 23:06:18 +08:00
cinwell.liandGitHub 69e500bd33 Fix repo (#55)
* Fix option is an empty string

* Fix test

* Add hr style #54

* Tweak hr style
2017-01-13 23:05:05 +08:00
qingwei.li 000a1fcdf0 -> v1.7.2 2017-01-12 21:31:40 +08:00
qingwei.li b84544960f bump 1.7.2 2017-01-12 21:31:22 +08:00
qingwei.li 28a004840d Fix sidebar click event in mobile browser 2017-01-12 21:30:33 +08:00
qingwei.li ff44152d12 -> v1.7.1 2017-01-12 20:46:24 +08:00
qingwei.li b6bfad8579 bump 1.7.1 2017-01-12 20:46:04 +08:00
qingwei.li e93f6b95ea Fix sidebar style in mobile browser 2017-01-12 20:45:35 +08:00
qingwei.li 5c58629e69 Fix index.html 2017-01-12 20:34:47 +08:00
qingwei.li 55826691eb -> v1.7.0 2017-01-12 20:30:13 +08:00
qingwei.li 64093c0567 bump 1.7.0 2017-01-12 20:29:45 +08:00
qingwei.li c998fad98e Add docs 2017-01-12 20:28:59 +08:00
qingwei.li 0a2e1ddf11 Tweak active item in sidebar 2017-01-12 20:23:55 +08:00
qingwei.li 4a7df99224 Add name link 2017-01-12 20:16:18 +08:00
qingwei.li 6c6f687925 Merge branch 'feat/data-name' 2017-01-12 19:48:21 +08:00
qingwei.li ddc13aa280 Add changelog 2017-01-12 19:46:59 +08:00
qingwei.li f997280ab1 Fix sticky sidebar 2017-01-12 19:46:28 +08:00
qingwei.li 458e262a26 Tweak style 2017-01-12 18:44:41 +08:00
qingwei.li d971948f10 Add yarn.lock 2017-01-12 18:44:41 +08:00
qingwei.li a309d3041e Update docs 2017-01-12 18:44:41 +08:00
qingwei.li 557a165349 fixed custom cover background, fixed #52 2017-01-12 18:44:41 +08:00
Chao Leeandcinwell.li d9eedf609e add showcase at README.md (#53) 2017-01-12 11:32:45 +08:00
qingwei.li aa939147c6 Add name option 2017-01-11 18:11:49 +08:00
qingwei.li 67c1d0cd7d Add yarn.lock 2017-01-11 16:53:08 +08:00
qingwei.li 06892e6de4 Update docs 2017-01-11 16:22:17 +08:00
qingwei.li 439a1e6841 fixed custom cover background, fixed #52 2017-01-11 16:18:43 +08:00
qingwei.li f540e3c4fc Add bash/markdown highlight 2017-01-10 23:55:58 +08:00
qingwei.li 2ffef062db Update version number 2017-01-10 23:52:32 +08:00
qingwei.li db7db5af8b -> v1.6.1 2017-01-10 23:49:06 +08:00
qingwei.li a418096a29 bump 1.6.1 2017-01-10 23:48:49 +08:00
qingwei.li 90b490c9c1 Fixed sidebar bug when the coverpage exist 2017-01-10 23:48:12 +08:00
qingwei.li 7915027418 -> v1.6.0 2017-01-10 23:34:20 +08:00
qingwei.li 87b373b841 bump 1.6.0 2017-01-10 23:33:41 +08:00
qingwei.li 510ae37a04 Add doc 2017-01-10 23:32:25 +08:00
qingwei.li ef53a07f49 Add new markdown grammar & improve sidebar performance. 2017-01-10 23:16:55 +08:00
35 changed files with 5333 additions and 437 deletions
+4 -1
View File
@@ -1,6 +1,9 @@
{
"extends": ["vue"],
"env": {
"browser": true
},
"globals": {
"XMLHttpRequest": true
"$docsify": true
}
}
-1
View File
@@ -1,5 +1,4 @@
.DS_Store
*.log
node_modules
yarn.lock
/themes
+127
View File
@@ -1,3 +1,130 @@
## 2.2.1
> 2017-02-11
### Bug fixes
- fix(search): crash when not content, fixed #68
- fix(event): scroll active sidebar
- fix(search): not work in mobile
## 2.2.0
### Features
- Add `Google Analytics` plugin.
```html
<script src="//unpkg.com/docsify" data-ga="UA-XXXXX-Y"></script>
<script src="//unpkg.com/docsify/lib/plugins/ga.js"></script>
```
## 2.1.0
### Features
- Add search plugin
```html
<script src="//unpkg.com/docsify"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.js"></script>
```
### Bug fixes
- fix sidebar style
## 2.0.3
### Bug fixes
- fix: rendering emojis
- fix: css var polyfill
## 2.0.2
### Bug fixes
- fix button style in cover page.
## 2.0.1
### Bug fixes
- border style.
## 2.0.0
### Features
- Customize the theme color
### Break change
- Remove `data-router`, `data-sidebar`, `data-sidebar-toggle` APIs
## 1.10.5
### Bug fixes
- fix initialize the Vue instance
## 1.10.4
### Bug fixes
- fix execute script
## 1.10.3
### Bug fixes
- compatible vuep QingWei-Li/vuep/issues/2
- fix sidebar scroll, fixed #63
## 1.10.2
### Bug fixes
- Fix render emojis again
## 1.10.1
### Bug fixes
- Fix render emojis
## 1.10.0
### Features
- Support emoji :laughing:
## 1.9.0
### Bug fixes
- Destroys the vue instance when the route is changed
### Features
- Add `!>` and `?>` doc helper.
### Break change
- Remove `!` doc helper.
## 1.8.0
### Bug fixes
- Using `v-pre` skip compilation.
### Features
- Execute script when vue exists.
## 1.7.4
### Bug fixes
- Fix bugs caused by the previous version
## 1.7.3
### Bug fixes
- Add `hr` style
- Fixed option is an empty string
## 1.7.2
### Bug fixes
- Fix sidebar click event in mobile browser.
## 1.7.1
### Bug fixes
- Fix sidebar style in mobile browser.
## 1.7.0
### Bug fixes
- Fixed custom cover background, fixed #52
- Fixed sticky sidebar
## Features
- Add `name` and `nameLink`
## 1.6.1
### Bug fixes
- Fixed sidebar bug when the coverpage exist
## 1.6.0
### Features
- Improve sidebar performance. The active item is automatically scrolled in the visible view.
- New doc helper: `! `. e.g. `! content` will be rendered as `<p class="tip">content</p>`
## 1.5.2
### Bug fixes
- Fixed number at the beginning of the slug
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016 cinwell.li
Copyright (c) 2016 - 2017 cinwell.li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+13 -5
View File
@@ -22,9 +22,10 @@
- Simple and lightweight (~13kB gzipped)
- Multiple themes
- Not build static html files
- Support emoji :laughing:
## Quick start
Create a `index.html` and using `hash router`.
Create a `index.html`.
index.html
@@ -38,7 +39,7 @@ index.html
<body>
<div id="app"></div>
</body>
<script src="//unpkg.com/docsify" data-router></script>
<script src="//unpkg.com/docsify"></script>
</html>
```
@@ -46,12 +47,20 @@ index.html
- UNPKG [https://unpkg.com/docsify/](https://unpkg.com/docsify/)
- jsDelivr [http://www.jsdelivr.com/projects/docsify](http://www.jsdelivr.com/projects/docsify)
## Browser Support
Modern browsers and Internet Explorer 9+.
## Showcase
These open-source projects are using docsify to generate their sites. Pull requests welcome : )
- [docsify](https://docsify.js.org) - A magical documentation site generator.
- [Snipaste](https://docs.snipaste.com/) - A new way to boost your productivity.
- [puck](https://puck.zz173.com/) - A small & magical php framework.
- [Samaritan](http://samaritan.stockdb.org) - An Algorithmic Trading Framework for Digital Currency.
- [Vudash](http://vudash.github.io/vudash/) - Powerful, Flexible, Open Source dashboards for anything
- [Trilogy](http://trilogy.js.org) - No-hassle SQLite with a Promise-based, document store style API.
- [Mybatis-Plus](http://mp.baomidou.com/) - An enhanced toolkit of Mybatis to simplify development
## Similar projects
- [docute](https://github.com/egoist/docute) - 📜 Effortlessly documentation done right
@@ -68,13 +77,12 @@ These open-source projects are using docsify to generate their sites. Pull reque
## Development
### prepare
```shell
```bash
npm i && npm run dev
open http://localhost:3000
```
### More Language Highlight
## More Language Highlight
```html
<script src="//unpkg.com/docsify"></script>
+11 -2
View File
@@ -2,8 +2,17 @@ var fs = require('fs')
var cssnano = require('cssnano').process
var resolve = require('path').resolve
var postcss = require('postcss')
var isProd = process.argv[process.argv.length - 1] !== '--dev'
var processor = postcss([require('postcss-salad')])
var processor = postcss([require('postcss-salad')({
features: {
precss: {
properties: {
preserve: true
}
}
}
})])
var saveMin = function (file, content) {
fs.writeFileSync(resolve(__dirname, '../lib/themes/', file), content)
@@ -26,7 +35,7 @@ list.forEach(function (file) {
.then(function (result) {
save(file, result.css)
console.log('salad - ' + file)
cssnano(loadLib(file))
isProd && cssnano(loadLib(file))
.then(function (result) {
saveMin(file, result.css)
console.log('cssnao - ' + file)
+25 -2
View File
@@ -3,6 +3,7 @@ var buble = require('rollup-plugin-buble')
var commonjs = require('rollup-plugin-commonjs')
var nodeResolve = require('rollup-plugin-node-resolve')
var uglify = require('rollup-plugin-uglify')
var isProd = process.argv[process.argv.length - 1] !== '--dev'
var build = function (opts) {
rollup
@@ -16,7 +17,7 @@ var build = function (opts) {
console.log(dest)
bundle.write({
format: 'iife',
moduleName: opts.moduleName || 'Docsify',
moduleName: opts.moduleName || 'D',
dest: dest
})
})
@@ -30,8 +31,30 @@ build({
output: 'docsify.js',
plugins: [commonjs(), nodeResolve()]
})
build({
isProd && build({
entry: 'index.js',
output: 'docsify.min.js',
plugins: [commonjs(), nodeResolve(), uglify()]
})
build({
entry: 'plugins/search.js',
output: 'plugins/search.js',
moduleName: 'D.Search'
})
isProd && build({
entry: 'plugins/search.js',
output: 'plugins/search.min.js',
moduleName: 'D.Search',
plugins: [uglify()]
})
build({
entry: 'plugins/ga.js',
output: 'plugins/ga.js',
moduleName: 'D.GA'
})
isProd && build({
entry: 'plugins/ga.js',
output: 'plugins/ga.min.js',
moduleName: 'D.GA',
plugins: [uglify()]
})
+19 -2
View File
@@ -5,8 +5,25 @@
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="/themes/vue.css">
</head>
<body class="">
<body>
<nav>
<a href="#/">En</a>
<a href="#/zh-cn">中文</a>
</nav>
<div id="app"></div>
</body>
<script src="/lib/docsify.js" data-repo="qingwei-li/docsify" data-auto2top data-sidebar-toggle data-router></script>
<script>
window.$docsify = {
search: {
maxAge: 0
}
}
</script>
<script
src="/lib/docsify.js"
data-repo="qingwei-li/docsify"
data-name="docsify"
data-base-path="docs/"
data-auto2top></script>
<script src="/lib/plugins/search.js"></script>
</html>
+169 -68
View File
@@ -2,7 +2,7 @@
### Create a project
First create a project, then create a `docs` folder
```shell
```bash
mkdir my-project && cd my-project
mkdir docs && cd docs
```
@@ -20,13 +20,13 @@ Create a `index.html` file
<body>
<div id="app"></div>
</body>
<script src="//unpkg.com/docsify" data-router></script>
<script src="//unpkg.com/docsify"></script>
</html>
```
Create `README.md` as the main page
```
```markdown
# Title
## blabla
@@ -41,20 +41,20 @@ Push code and activate **GitHub Pages** via your repo's settings
Easy to setup and preview a docs.
### Install
```shell
```bash
npm i docsify-cli -g
```
### Setup
Setup a boilerplate docs
```shell
```bash
docsify init docs
```
### Preview
Preview and serve your docs using
```shell
```bash
docsify serve docs
```
@@ -157,6 +157,65 @@ window.$docsify = {
}
```
### Doc Helpers
#### p.tip
`!> ` add your content will rendered as `<p class="tip">content</p>`
```markdown
!> Important **information**
```
It will be rendered
```html
<p class="tip">Important <strong>information</strong></p>
```
e.g.
!> Important **information**
#### p.warn
```markdown
?> todo info
```
?> todo info
### Combining Vue
We can write the Vue syntax directly in the markdown file, when the Vue library is loaded into `index.html`. Default will automatically initialize a Vue instance, of course, we can also manually.
index.html
```html
<script src="//unpkg.com/vue"></script>
<script src="//unpkg.com/docsify"></script>
```
```markdown
<ul>
<li v-for="i in 10">{{ i }}</li>
</ul>
```
Manual initialization
```markdown
<div>
<input type="text" v-model="msg">
<p>Hello, {{ msg }}</p>
</div>
<script>
new Vue({
el: 'main',
data: { msg: 'Docsify' }
})
</script>
```
## Options
You can add configurations in the script tag attributes or with `window.$docsify`.
@@ -201,50 +260,6 @@ window.$docsify = {
}
```
### sidebar-toggle
Sidebar with toggle
```html
<script src="//unpkg.com/docsify" data-sidebar-toggle></script>
```
```js
window.$docsify = {
sidebarToggle: true
}
```
### 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)
```html
<script>
window.sidebar = [
{ slug: '/', title: 'Home' },
{
slug: '/pageA',
title: 'page A',
children: [
{ slug: '/pageA/childrenB', title: 'children B' }
]
},
{ slug: '/PageC', title: 'Page C' }
]
</script>
<script src="/lib/docsify.js" data-sidebar="sidebar"></script>
```
```js
window.$docsify = {
sidebar: 'sidebar'
}
```
### load-sidebar
Load sidebar markdown file. If it is configured, load the current directory `_sidebar.md` by default. If the file doesn't exist, the sidebar will appear as a TOC.
@@ -329,20 +344,6 @@ If you write a sub level list, it will generate a dropdown list.
- [chinese](/zh-cn)
```
### router
Hash router.
```html
<script src="/lib/docsify.js" data-router></script>
```
```js
window.$docsify = {
router: true
}
```
### auto2top
@@ -351,17 +352,20 @@ Scroll to the top on changing hash.
```html
<script src="/lib/docsify.js" data-auto2top></script>
<!-- Set offset top -->
<script src="/lib/docsify.js" data-auto2top="50"></script>
```
```js
window.$docsify = {
auto2top: true
auto2top: true,
// auto2top: 50
}
```
### homepage
`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`):
`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.
```html
<script src="/lib/docsify.js" data-homepage="https://raw.githubusercontent.com/QingWei-Li/docsify/master/README.md"></script>
@@ -377,7 +381,7 @@ window.$docsify = {
```
### basePath
### base-path
If your HTML entry file and the markdown files are in different directories, we can use:
@@ -410,4 +414,101 @@ Generate cover page.
window.$docsify = {
coverpage: true
}
```
```
### name
Project name. It is displayed in the sidebar.
```html
<script src="/lib/docsify.js" data-name="docsify"></script>
```
```js
window.$docsify = {
name: 'docsify'
}
```
### name-link
Name link. The default value is `window.location.pathname`.
```html
<script src="/lib/docsify.js" data-name-link="/"></script>
```
```js
window.$docsify = {
nameLink: '/'
}
```
### theme-color
Customize the theme color.
```html
<script src="/lib/docsify.js" data-theme-color="#3F51B5"></script>
```
```js
window.$docsify = {
themeColor: '#3F51B5'
}
```
## Plugins
### Full Text Search
If a document can have a search, can enhance some user experience. Installing the search plugin is easy. Such as
```html
<script src="//unpkg.com/docsify"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.js"></script>
```
By default, the hyperlink on the current page is recognized and the content is saved in `localStorage`. You can also specify the path to the files.
!> Configure the content before the plugin is installed.
```js
window.$docsify = {
search: 'auto', // default
search : [
'/', // => /README.md
'/guide', // => /guide.md
'/get-started', // => /get-started.md
'/zh-cn/', // => /zh-cn/README.md
],
// Full configuration
search: {
maxAge: 86400000, // Expiration time, the default one day
paths: [], // or 'auto'
placeholder: 'Type to search'
}
}
```
### Google Analytics
Install the plugin and configure the track id.
```html
<script src="//unpkg.com/docsify" data-ga="UA-XXXXX-Y"></script>
<script src="//unpkg.com/docsify/lib/plugins/ga.js"></script>
```
or
```js
window.$docsify = {
ga: 'UA-XXXXX-Y'
}
```
+3 -2
View File
@@ -1,12 +1,13 @@
![logo](_media/icon.svg)
# docsify <small>1.5.2</small>
# docsify <small>2.2.1</small>
> A magical documentation site generator.
- Simple and lightweight (~13kB gzipped)
- Multiple themes
- Not build static html files
- Support emoji :laughing:
- Multiple themes
[GitHub](https://github.com/QingWei-Li/docsify/)
+4 -2
View File
@@ -18,7 +18,9 @@
<script
src="//unpkg.com/docsify/lib/docsify.min.js"
data-max-level="4"
data-sidebar-toggle
data-coverpage
data-router></script>
data-name="docsify"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-markdown.min.js"></script>
</html>
+165 -66
View File
@@ -8,7 +8,7 @@
### 创建项目
新建一个空项目,接着创建一个 `docs` 目录并进入到 docs 目录下
```shell
```bash
mkdir my-project && cd my-project
mkdir docs && cd docs
```
@@ -25,7 +25,7 @@ mkdir docs && cd docs
<body>
<div id="app"></div>
</body>
<script src="//unpkg.com/docsify" data-router></script>
<script src="//unpkg.com/docsify"></script>
</html>
```
@@ -46,20 +46,20 @@ mkdir docs && cd docs
方便快速创建文档目录,会读取项目的 `package.json` 里的选项作为 docsify 的配置,支持本地预览。
### 安装
```shell
```bash
npm i docsify-cli -g
```
### 初始化文档
默认初始化在当前目录,推荐将文档放在 `docs` 目录下
```shell
```bash
docsify init docs
```
### 启动本地服务
启动一个 server 方便预览,打开 http://localhost:3000
```shell
```bash
docsify serve docs
```
@@ -163,6 +163,68 @@ window.$docsify = {
}
```
### 文档助手
#### 内置「提示」语法
`!>`后面接内容,会渲染成带 tip 类名的段落。
```markdown
!> 提示信息,**支持其他 markdown 语法**
```
将被渲染成
```html
<p class="tip">提示信息<strong>支持其他 markdown 语法</strong></p>
```
效果
!> 适合显示醒目的内容
#### 内置「警示」语法
`?>`后面接内容,会渲染成带 warn 类名的段落。
```markdown
?> 警示内容样式
```
效果
?> 警示内容样式
### 结合 Vue
`index.html` 内引入 Vue 后,可以在文档里直接写 Vue 语法。默认会自己初始化一个 Vue 示例,当然我们也可以手动初始化一个实例。
index.html
```html
<script src="//unpkg.com/vue"></script>
<script src="//unpkg.com/docsify"></script>
```
```markdown
<ul>
<li v-for="i in 10">{{ i }}</li>
</ul>
```
手动初始化示例
```markdown
<div>
<input type="text" v-model="msg">
<p>Hello, {{ msg }}</p>
</div>
<script>
new Vue({
el: 'main',
data: { msg: 'Docsify' }
})
</script>
```
## 配置参数
@@ -209,51 +271,6 @@ window.$docsify = {
}
```
### sidebar-toggle
Sidebar 开关按钮
```html
<script src="//unpkg.com/docsify" data-sidebar-toggle></script>
```
```js
window.$docsify = {
sidebarToggle: true
}
```
### sidebar
设置后 TOC 功能将不可用,适合导航较多的文档,`data-sidebar` 传入全局变量名。
![image](https://cloud.githubusercontent.com/assets/7565692/20647425/de5ab1c2-b4ce-11e6-863a-135868f2f9b4.png)
```html
<script>
window.sidebar = [
{ slug: '/', title: 'Home' },
{
slug: '/pageA',
title: 'page A',
children: [
{ slug: '/pageA/childrenB', title: 'children B' }
]
},
{ slug: '/PageC', title: 'Page C' }
]
</script>
<script src="/lib/docsify.js" data-sidebar="sidebar"></script>
```
```js
window.$docsify = {
sidebar: 'sidebar'
}
```
### load-sidebar
读取侧边栏配置文件,如果配置,默认加载当前目录下的 `_sidebar.md`。如果文件不存在,会显示 TOC 作为侧边栏内容。如果你有二级目录,也应该放置一份配置文件。
@@ -338,20 +355,6 @@ window.$docsify = {
```
### router
开启 hash router 功能,同时多页面切换不会重新加载资源。资源路径会被替换成 `/#/` 的形式。
```html
<script src="/lib/docsify.js" data-router></script>
```
```js
window.$docsify = {
router: true
}
```
### auto2top
切换路由时自动跳转到页面顶部
@@ -369,7 +372,7 @@ window.$docsify = {
### homepage
默认情况下网站会将根目录下 `README.md` 作为首页渲染,但是有些时候我们想指定其他文件,甚至想直接将 repo 下的 README 作为首页。你可以这样做(需要设置 `data-router`):
默认情况下网站会将根目录下 `README.md` 作为首页渲染,但是有些时候我们想指定其他文件,甚至想直接将 repo 下的 README 作为首页。
```html
@@ -384,7 +387,7 @@ window.$docsify = {
}
```
### basePath
### base-path
指定加载文档的路径,如果你的 HTML 入口文件和文档是放在不同地方,你可以设置:
@@ -419,3 +422,99 @@ window.$docsify = {
coverpage: true
}
```
### name
项目名,将显示在侧边栏。
```html
<script src="/lib/docsify.js" data-name="docsify"></script>
```
```js
window.$docsify = {
name: 'docsify'
}
```
### name-link
项目名链接,默认为 `window.location.pathname`
```html
<script src="/lib/docsify.js" data-name-link="/"></script>
```
```js
window.$docsify = {
nameLink: '/'
}
```
### theme-color
自定义主题色。
```html
<script src="/lib/docsify.js" data-theme-color="#3F51B5"></script>
```
```js
window.$docsify = {
themeColor: '#3F51B5'
}
```
## Plugins
### 全文检索 - search
一份文档如果能有搜索功能会提升一些用户体验,加载搜索插件也很简单,直接引入就自动安装并启用。默认情况下会自动分析当前页面上的超链接,获取内容后建立索引并存储在 `localStorage` 里,默认过期时间为一天,当然这是可配置的。
自动识别的方式不一定能识别完整或者如果你想指定某些文件可索引,你可以自己指定文件的路径。
```html
<script src="//unpkg.com/docsify/lib/docsify.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.js"></script>
```
!> 配置要在 docsify 引入之前
```js
window.$docsify = {
search: 'auto', // default
search : [
'/', // => /README.md
'/guide', // => /guide.md
'/get-started', // => /get-started.md
'/zh-cn/', // => /zh-cn/README.md
],
// Full configuration
search: {
maxAge: 86400000, // Expiration time, the default one day
paths: [], // or 'auto'
placeholder: 'Type to search'
}
}
```
### Google Analytics
安装插件并且配置 track id。
```html
<script src="//unpkg.com/docsify" data-ga="UA-XXXXX-Y"></script>
<script src="//unpkg.com/docsify/lib/plugins/ga.js"></script>
```
或者
```js
window.$docsify = {
ga: 'UA-XXXXX-Y'
}
```
+285 -133
View File
@@ -1,4 +1,4 @@
var Docsify = (function () {
var D = (function () {
'use strict';
/**
@@ -97,7 +97,7 @@ function getRoute () {
var loc = window.location;
if (cacheHash === loc.hash && !isNil(cacheRoute$1)) { return cacheRoute$1 }
var route = loc.hash.match(/^#\/([^#]+)/);
var route = loc.hash.replace(/%23/g, '#').match(/^#\/([^#]+)/);
if (route && route.length === 2) {
route = route[1];
@@ -168,6 +168,27 @@ var merge = Object.assign || function (to) {
return to
};
function emojify (text) {
return text
.replace(/<(pre|template)[^>]*?>([\s\S]+)<\/(pre|template)>/g, function (match) { return match.replace(/:/g, '__colon__'); })
.replace(/:(\w*?):/ig, '<img class="emoji" src="https://assets-cdn.github.com/images/icons/emoji/$1.png" alt="$1" />')
.replace(/__colon__/g, ':')
}
var utils = Object.freeze({
load: load,
genTree: genTree,
camel2kebab: camel2kebab,
isNil: isNil,
getRoute: getRoute,
isMobile: isMobile,
slugify: slugify,
merge: merge,
emojify: emojify
});
/**
* Active sidebar when scroll
* @link https://buble.surge.sh/
@@ -175,10 +196,15 @@ var merge = Object.assign || function (to) {
function scrollActiveSidebar () {
if (isMobile()) { return }
var hoveredOverSidebar = false;
var anchors = document.querySelectorAll('.anchor');
var sidebar = document.querySelector('.sidebar');
var sidebarContainer = sidebar.querySelector('.sidebar-nav');
var sidebarHeight = sidebar.clientHeight;
var nav = {};
var lis = document.querySelectorAll('.sidebar li');
var active = null;
var lis = sidebar.querySelectorAll('li');
var active = sidebar.querySelector('li.active');
for (var i = 0, len = lis.length; i < len; i += 1) {
var li = lis[i];
@@ -193,27 +219,52 @@ function scrollActiveSidebar () {
}
function highlight () {
var top = document.body.scrollTop;
var last;
for (var i = 0, len = anchors.length; i < len; i += 1) {
var node = anchors[i];
var bcr = node.getBoundingClientRect();
if (bcr.top < 10 && bcr.bottom > 10) {
var li = nav[node.getAttribute('data-id')];
if (!li || li === active) { return }
if (active) { active.setAttribute('class', ''); }
li.setAttribute('class', 'active');
active = li;
return
if (node.offsetTop > top) {
if (!last) { last = node; }
break
} else {
last = node;
}
}
if (!last) { return }
var li = nav[last.getAttribute('data-id')];
if (!li || li === active) { return }
if (active) { active.classList.remove('active'); }
li.classList.add('active');
active = li;
// scroll into view
// https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297
if (!hoveredOverSidebar && !sticky.noSticky) {
var currentPageOffset = 0;
var currentActiveOffset = active.offsetTop + active.clientHeight + 40;
var currentActiveIsInView = (
active.offsetTop >= sidebarContainer.scrollTop &&
currentActiveOffset <= sidebarContainer.scrollTop + sidebarHeight
);
var linkNotFurtherThanSidebarHeight = currentActiveOffset - currentPageOffset < sidebarHeight;
var newScrollTop = currentActiveIsInView
? sidebarContainer.scrollTop
: linkNotFurtherThanSidebarHeight
? currentPageOffset
: currentActiveOffset - sidebarHeight;
sidebar.scrollTop = newScrollTop;
}
}
window.removeEventListener('scroll', highlight);
window.addEventListener('scroll', highlight);
highlight();
sidebar.addEventListener('mouseover', function () { hoveredOverSidebar = true; });
sidebar.addEventListener('mouseleave', function () { hoveredOverSidebar = false; });
}
function scrollIntoView () {
@@ -221,7 +272,9 @@ function scrollIntoView () {
if (!id || !id.length) { return }
var section = document.querySelector(decodeURIComponent(id[0]));
if (section) { section.scrollIntoView(); }
if (section) { setTimeout(function () { return section.scrollIntoView(); }, 0); }
return section
}
/**
@@ -237,13 +290,13 @@ function activeLink (dom, activeParent) {
.forEach(function (node) {
if (host.indexOf(node.href) === 0 && !target) {
activeParent
? node.parentNode.setAttribute('class', 'active')
: node.setAttribute('class', 'active');
? node.parentNode.classList.add('active')
: node.classList.add('active');
target = node;
} else {
activeParent
? node.parentNode.removeAttribute('class')
: node.removeAttribute('class');
? node.parentNode.classList.remove('active')
: node.classList.remove('active');
}
});
@@ -261,24 +314,31 @@ function bindToggle (dom) {
dom.addEventListener('click', function () { return body.classList.toggle('close'); });
if (isMobile()) {
document.querySelector('aside')
.addEventListener('click', function (_) { return body.classList.toggle('close'); });
var sidebar = document.querySelector('.sidebar');
sidebar.addEventListener('click', function () {
body.classList.toggle('close');
setTimeout(function () { return activeLink(sidebar, true); }, 0);
});
}
}
function scroll2Top () {
document.body.scrollTop = 0;
function scroll2Top (offset) {
if ( offset === void 0 ) offset = 0;
document.body.scrollTop = offset === true ? 0 : Number(offset);
}
function sticky () {
var dom = document.querySelector('section.cover');
var coverHeight = dom.getBoundingClientRect().height;
sticky.dom = sticky.dom || document.querySelector('section.cover');
var coverHeight = sticky.dom.getBoundingClientRect().height;
return (function () {
if (window.pageYOffset >= coverHeight || dom.classList.contains('hidden')) {
if (window.pageYOffset >= coverHeight || sticky.dom.classList.contains('hidden')) {
document.body.classList.add('sticky');
sticky.noSticky = false;
} else {
document.body.classList.remove('sticky');
sticky.noSticky = true;
}
})()
}
@@ -1581,12 +1641,8 @@ marked.inlineLexer = InlineLexer.output;
marked.parse = marked;
if (typeof module !== 'undefined' && typeof exports === 'object') {
{
module.exports = marked;
} else if (typeof define === 'function' && define.amd) {
define(function() { return marked; });
} else {
this.marked = marked;
}
}).call(function() {
@@ -1804,6 +1860,9 @@ var _ = _self.Prism = {
_.hooks.run('before-sanity-check', env);
if (!env.code || !env.grammar) {
if (env.code) {
env.element.textContent = env.code;
}
_.hooks.run('complete', env);
return;
}
@@ -1881,9 +1940,16 @@ var _ = _self.Prism = {
lookbehindLength = 0,
alias = pattern.alias;
if (greedy && !pattern.pattern.global) {
// Without the global flag, lastIndex won't work
var flags = pattern.pattern.toString().match(/[imuy]*$/)[0];
pattern.pattern = RegExp(pattern.pattern.source, flags + "g");
}
pattern = pattern.pattern || pattern;
for (var i=0; i<strarr.length; i++) { // Dont cache length as it changes during the loop
// Dont cache length as it changes during the loop
for (var i=0, pos = 0; i<strarr.length; pos += strarr[i].length, ++i) {
var str = strarr[i];
@@ -1903,40 +1969,38 @@ var _ = _self.Prism = {
// Greedy patterns can override/remove up to two previously matched tokens
if (!match && greedy && i != strarr.length - 1) {
// Reconstruct the original text using the next two tokens
var nextToken = strarr[i + 1].matchedStr || strarr[i + 1],
combStr = str + nextToken;
if (i < strarr.length - 2) {
combStr += strarr[i + 2].matchedStr || strarr[i + 2];
}
// Try the pattern again on the reconstructed text
pattern.lastIndex = 0;
match = pattern.exec(combStr);
pattern.lastIndex = pos;
match = pattern.exec(text);
if (!match) {
continue;
break;
}
var from = match.index + (lookbehind ? match[1].length : 0);
// To be a valid candidate, the new match has to start inside of str
if (from >= str.length) {
var from = match.index + (lookbehind ? match[1].length : 0),
to = match.index + match[0].length,
k = i,
p = pos;
for (var len = strarr.length; k < len && p < to; ++k) {
p += strarr[k].length;
// Move the index i to the element in strarr that is closest to from
if (from >= p) {
++i;
pos = p;
}
}
/*
* If strarr[i] is a Token, then the match starts inside another Token, which is invalid
* If strarr[k - 1] is greedy we are in conflict with another greedy pattern
*/
if (strarr[i] instanceof Token || strarr[k - 1].greedy) {
continue;
}
var to = match.index + match[0].length,
len = str.length + nextToken.length;
// Number of tokens to delete and replace with the new match
delNum = 3;
if (to <= len) {
if (strarr[i + 1].greedy) {
continue;
}
delNum = 2;
combStr = combStr.slice(0, len);
}
str = combStr;
delNum = k - i;
str = text.slice(pos, p);
match.index -= pos;
}
if (!match) {
@@ -2005,7 +2069,7 @@ var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
this.content = content;
this.alias = alias;
// Copy of the full string this token was created from
this.matchedStr = matchedStr || null;
this.length = (matchedStr || "").length|0;
this.greedy = !!greedy;
};
@@ -2041,13 +2105,11 @@ Token.stringify = function(o, language, parent) {
_.hooks.run('wrap', env);
var attributes = '';
var attributes = Object.keys(env.attributes).map(function(name) {
return name + '="' + (env.attributes[name] || '').replace(/"/g, '&quot;') + '"';
}).join(' ');
for (var name in env.attributes) {
attributes += (attributes ? ' ' : '') + name + '="' + (env.attributes[name] || '') + '"';
}
return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + '</' + env.tag + '>';
return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>';
};
@@ -2080,7 +2142,11 @@ if (script) {
if (document.addEventListener && !script.hasAttribute('data-manual')) {
if(document.readyState !== "loading") {
requestAnimationFrame(_.highlightAll, 0);
if (window.requestAnimationFrame) {
window.requestAnimationFrame(_.highlightAll);
} else {
window.setTimeout(_.highlightAll, 16);
}
}
else {
document.addEventListener('DOMContentLoaded', _.highlightAll);
@@ -2092,7 +2158,7 @@ return _self.Prism;
})();
if (typeof module !== 'undefined' && module.exports) {
if ('object' !== 'undefined' && module.exports) {
module.exports = Prism;
}
@@ -2109,10 +2175,10 @@ if (typeof commonjsGlobal !== 'undefined') {
Prism.languages.markup = {
'comment': /<!--[\w\W]*?-->/,
'prolog': /<\?[\w\W]+?\?>/,
'doctype': /<!DOCTYPE[\w\W]+?>/,
'doctype': /<!DOCTYPE[\w\W]+?>/i,
'cdata': /<!\[CDATA\[[\w\W]*?]]>/i,
'tag': {
pattern: /<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,
pattern: /<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,
inside: {
'tag': {
pattern: /^<\/?[^\s>\/]+/i,
@@ -2169,7 +2235,10 @@ Prism.languages.css = {
},
'url': /url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,
'selector': /[^\{\}\s][^\{\};]*?(?=\s*\{)/,
'string': /("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,
'string': {
pattern: /("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,
greedy: true
},
'property': /(\b|\B)[\w-]+(?=\s*:)/i,
'important': /\B!important\b/i,
'function': /[-a-z0-9]+(?=\()/i,
@@ -2250,7 +2319,8 @@ Prism.languages.javascript = Prism.languages.extend('clike', {
'keyword': /\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,
'number': /\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,
// Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
'function': /[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i
'function': /[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i,
'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*\*?|\/|~|\^|%|\.{3}/
});
Prism.languages.insertBefore('javascript', 'keyword', {
@@ -2393,8 +2463,8 @@ function corner (data) {
/**
* Render main content
*/
function main (tpl) {
var aside = tpl + "<aside class=\"sidebar\"></aside>";
function main () {
var aside = (toggle()) + "<aside class=\"sidebar\"><div class=\"sidebar-nav\"></div></aside>";
return (isMobile() ? (aside + "<main>") : ("<main>" + aside)) +
"<section class=\"content\">\n <article class=\"markdown-section\"></article>\n </section>\n </main>"
@@ -2435,7 +2505,41 @@ function tree (toc, tpl) {
return tpl
}
var OPTIONS$1 = {};
function helper (className, content) {
return ("<p class=\"" + className + "\">" + (content.slice(5).trim()) + "</p>")
}
function theme (color) {
return ("<style>:root{--theme-color: " + color + ";}</style>")
}
function replaceVar (block) {
block.innerHTML = block.innerHTML.replace(/var\(\s*--theme-color.*?\)/g, $docsify.themeColor);
}
function cssVars () {
// variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) { return }
var styleBlocks = document.querySelectorAll('style:not(.inserted),link');[].forEach.call(styleBlocks, function (block) {
if (block.nodeName === 'STYLE') {
replaceVar(block);
} else if (block.nodeName === 'LINK') {
var href = block.getAttribute('href');
if (!/\.css$/.test(href)) { return }
load(href).then(function (res) {
var style = document.createElement('style');
style.innerHTML = res;
document.head.appendChild(style);
replaceVar(style);
});
}
});
}
var markdown = marked;
var toc = [];
var CACHE = {};
@@ -2450,11 +2554,8 @@ var renderTo = function (dom, content) {
/**
* init render
* @param {Object} options
*/
function init (options) {
OPTIONS$1 = options;
function init () {
var renderer = new marked.Renderer();
/**
* render anchor tag
@@ -2464,10 +2565,7 @@ function init (options) {
var slug = slugify(text);
var route = '';
if (OPTIONS$1.router) {
route = "#/" + (getRoute());
}
route = "#/" + (getRoute());
toc.push({ level: 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 + ">")
@@ -2476,25 +2574,42 @@ function init (options) {
renderer.code = function (code, lang) {
if ( lang === void 0 ) lang = '';
var hl = prism.highlight(code, prism.languages[lang] || prism.languages.markup)
.replace(/{{/g, '<span>{{</span>');
var hl = prism.highlight(code, prism.languages[lang] || prism.languages.markup);
return ("<pre data-lang=\"" + lang + "\"><code class=\"lang-" + lang + "\">" + hl + "</code></pre>")
return ("<pre v-pre data-lang=\"" + lang + "\"><code class=\"lang-" + lang + "\">" + hl + "</code></pre>")
};
renderer.link = function (href, title, text) {
if (OPTIONS$1.router && !/:/.test(href)) {
href = ("#/" + href).replace(/\/\//g, '/');
if (!/:/.test(href)) {
href = ("#/" + href).replace(/\/+/g, '/');
}
return ("<a href=\"" + href + "\" title=\"" + (title || '') + "\">" + text + "</a>")
};
renderer.paragraph = function (text) {
if (/^!&gt;/.test(text)) {
return helper('tip', text)
} else if (/^\?&gt;/.test(text)) {
return helper('warn', text)
}
return ("<p>" + text + "</p>")
};
if (typeof OPTIONS$1.markdown === 'function') {
if (typeof $docsify.markdown === 'function') {
markdown.setOptions({ renderer: renderer });
markdown = OPTIONS$1.markdown.call(this, markdown);
markdown = $docsify.markdown.call(this, markdown);
} else {
markdown.setOptions(merge({ renderer: renderer }, OPTIONS$1.markdown));
markdown.setOptions(merge({ renderer: renderer }, $docsify.markdown));
}
var md = markdown;
markdown = function (text) { return emojify(md(text)); };
window.Docsify.utils.marked = function (text) {
var result = markdown(text);
toc = [];
return result
};
}
/**
@@ -2502,21 +2617,35 @@ function init (options) {
*/
function renderApp (dom, replace) {
var nav = document.querySelector('nav') || document.createElement('nav');
var body = document.body;
var head = document.head;
if (!OPTIONS$1.repo) { nav.classList.add('no-badge'); }
if (!$docsify.repo) { nav.classList.add('no-badge'); }
dom[replace ? 'outerHTML' : 'innerHTML'] = corner(OPTIONS$1.repo) +
(OPTIONS$1.coverpage ? cover() : '') +
main(OPTIONS$1.sidebarToggle ? toggle() : '');
document.body.insertBefore(nav, document.body.children[0]);
dom[replace ? 'outerHTML' : 'innerHTML'] = corner($docsify.repo) +
($docsify.coverpage ? cover() : '') +
main();
body.insertBefore(nav, body.children[0]);
// theme color
if ($docsify.themeColor) {
head.innerHTML += theme($docsify.themeColor);
cssVars();
}
// render name
if ($docsify.name) {
var aside = document.querySelector('.sidebar');
aside.innerHTML = "<h1><a href=\"" + ($docsify.nameLink) + "\">" + ($docsify.name) + "</a></h1>" + aside.innerHTML;
}
// bind toggle
bindToggle('button.sidebar-toggle');
// bind sticky effect
if (OPTIONS$1.coverpage) {
if ($docsify.coverpage) {
!isMobile() && window.addEventListener('scroll', sticky);
} else {
document.body.classList.add('sticky');
body.classList.add('sticky');
}
}
@@ -2525,13 +2654,24 @@ function renderApp (dom, replace) {
*/
function renderArticle (content) {
renderTo('article', content ? markdown(content) : 'not found');
if (!OPTIONS$1.sidebar && !OPTIONS$1.loadSidebar) { renderSidebar(); }
if (!$docsify.loadSidebar) { renderSidebar(); }
if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') {
var vm = new Vue({ el: 'main' }); // eslint-disable-line
vm.$nextTick(function (_) { return scrollActiveSidebar(); });
if (content && typeof Vue !== 'undefined') {
CACHE.vm && CACHE.vm.$destroy();
var script = [].slice.call(
document.body.querySelectorAll('article>script'))
.filter(function (script) { return !/template/.test(script.type); }
)[0];
var 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(function (_) { return scrollActiveSidebar(); });
}
if (OPTIONS$1.auto2top) { scroll2Top(); }
if ($docsify.auto2top) { setTimeout(function () { return scroll2Top($docsify.auto2top); }, 0); }
}
/**
@@ -2553,23 +2693,23 @@ function renderSidebar (content) {
if (content) {
html = markdown(content);
} else if (OPTIONS$1.sidebar) {
html = tree(OPTIONS$1.sidebar, '<ul>');
// find url tag
html = html.match(/<ul[^>]*>([\s\S]+)<\/ul>/g)[0];
} else {
html = tree(genTree(toc, OPTIONS$1.maxLevel), '<ul>');
html = tree(genTree(toc, $docsify.maxLevel), '<ul>');
}
renderTo('aside.sidebar', html);
var target = activeLink('aside.sidebar', true);
if (content) { renderSubSidebar(target); }
renderTo('.sidebar-nav', html);
var target = activeLink('.sidebar-nav', true);
if (target) { renderSubSidebar(target); }
toc = [];
scrollActiveSidebar();
}
function renderSubSidebar (target) {
if (!OPTIONS$1.subMaxLevel) { return }
target.parentNode.innerHTML += tree(genTree(toc, OPTIONS$1.subMaxLevel), '<ul>');
if (!$docsify.subMaxLevel) { return }
target.parentNode.innerHTML += tree(genTree(toc, $docsify.subMaxLevel), '<ul>');
}
/**
@@ -2582,18 +2722,21 @@ function renderCover (content) {
return
}
renderCover.dom.classList.add('show');
if (renderCover.rendered) { return }
if (renderCover.rendered) { return sticky() }
// render cover
var cacheToc = toc.slice();
var html = markdown(content);
var match = html.trim().match('<p><img[^s]+src="(.*?)"[^a]+alt="(.*?)"></p>$');
var match = html.trim().match('<p><img[^s]+src="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$');
toc = cacheToc.slice();
// render background
if (match) {
var coverEl = document.querySelector('section.cover');
if (match[2] === 'color') {
coverEl.style.background = match[1];
coverEl.style.background = match[1] + (match[3] || '');
} else {
coverEl.classList.add('has-mask');
coverEl.style.backgroundImage = "url(" + (match[1]) + ")";
@@ -2649,15 +2792,16 @@ var OPTIONS = merge({
repo: '',
maxLevel: 6,
subMaxLevel: 0,
sidebar: '',
sidebarToggle: false,
loadSidebar: null,
loadNavbar: null,
router: false,
homepage: 'README.md',
coverpage: '',
basePath: '',
auto2top: false
auto2top: false,
name: '',
themeColor: '',
nameLink: window.location.pathname,
ga: ''
}, window.$docsify);
var script = document.currentScript || [].slice.call(document.getElementsByTagName('script')).pop();
@@ -2670,11 +2814,19 @@ if (script) {
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.sidebar) { OPTIONS.sidebar = window[OPTIONS.sidebar]; }
if (OPTIONS.repo === true) { OPTIONS.repo = ''; }
if (OPTIONS.name === true) { OPTIONS.name = ''; }
}
// utils
window.$docsify = OPTIONS;
window.Docsify = {
installed: true,
utils: merge({}, utils)
};
// load options
init(OPTIONS);
init();
var cacheRoute = null;
var cacheXhr = null;
@@ -2692,6 +2844,7 @@ var mainRender = function (cb) {
}
var page;
if (!route) {
page = OPTIONS.homepage || 'README.md';
} else if (/\/$/.test(route)) {
@@ -2714,11 +2867,10 @@ var mainRender = function (cb) {
if (OPTIONS.coverpage && page !== OPTIONS.homepage) { renderCover(); }
// render sidebar
if (OPTIONS.loadSidebar) {
load(basePath + OPTIONS.loadSidebar)
.then(function (result) {
renderSidebar(result);
cb();
});
var renderSidebar$$1 = function (result) { renderSidebar(result); cb(); };
load(basePath + OPTIONS.loadSidebar).then(renderSidebar$$1,
function (_) { return load(OPTIONS.loadSidebar).then(renderSidebar$$1); });
} else {
cb();
}
@@ -2726,7 +2878,8 @@ var mainRender = function (cb) {
// Render navbar
if (OPTIONS.loadNavbar) {
load(basePath + OPTIONS.loadNavbar).then(renderNavbar);
load(basePath + OPTIONS.loadNavbar).then(renderNavbar,
function (_) { return load(OPTIONS.loadNavbar).then(renderNavbar); });
}
};
@@ -2736,17 +2889,16 @@ var Docsify = function () {
var main = function () {
mainRender(function (_) {
scrollIntoView();
activeLink('nav');
activeLink('nav')
;[].concat(window.$docsify.plugins).forEach(function (fn) { return fn && fn(); });
});
};
// Render app
renderApp(dom, replace);
main();
if (OPTIONS.router) {
if (!/^#\//.test(window.location.hash)) { window.location.hash = '#/'; }
window.addEventListener('hashchange', main);
}
if (!/^#\//.test(window.location.hash)) { window.location.hash = '#/'; }
window.addEventListener('hashchange', main);
};
var index = Docsify();
+2 -2
View File
File diff suppressed because one or more lines are too long
+50
View File
@@ -0,0 +1,50 @@
this.D = this.D || {};
this.D.GA = (function () {
'use strict';
// From https://github.com/egoist/vue-ga/blob/master/src/index.js
function appendScript () {
var script = document.createElement('script');
script.async = true;
script.src = 'https://www.google-analytics.com/analytics.js';
document.body.appendChild(script);
}
function init (id) {
if (!window.ga) {
appendScript();
window.ga = window.ga || function () {
(window.ga.q = window.ga.q || []).push(arguments);
};
window.ga.l = Number(new Date());
window.ga('create', id, 'auto');
}
}
function collect () {
init(window.$docsify.ga);
window.ga('set', 'page', location.href);
window.ga('send', 'pageview');
}
var install = function () {
if (!window.Docsify || !window.Docsify.installed) {
console.error('[Docsify] Please load docsify.js first.');
return
}
if (!window.$docsify.ga) {
console.error('[Docsify] ga is required.');
return
}
collect();
window.$docsify.plugins = [].concat(window.$docsify.plugins, collect);
};
var ga = install();
return ga;
}());
+1
View File
@@ -0,0 +1 @@
this.D=this.D||{},this.D.GA=function(){"use strict";function o(){var o=document.createElement("script");o.async=!0,o.src="https://www.google-analytics.com/analytics.js",document.body.appendChild(o)}function i(i){window.ga||(o(),window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=Number(new Date),window.ga("create",i,"auto"))}function n(){i(window.$docsify.ga),window.ga("set","page",location.href),window.ga("send","pageview")}var w=function(){return window.Docsify&&window.Docsify.installed?window.$docsify.ga?(n(),void(window.$docsify.plugins=[].concat(window.$docsify.plugins,n))):void console.error("[Docsify] ga is required."):void console.error("[Docsify] Please load docsify.js first.")},s=w();return s}();
+287
View File
@@ -0,0 +1,287 @@
this.D = this.D || {};
this.D.Search = (function () {
'use strict';
var INDEXS = {};
var CONFIG = {
placeholder: 'Type to search',
paths: 'auto',
maxAge: 86400000 // 1 day
};
var isObj = function (obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
};
var escapeHtml = function (string) {
var entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;'
};
return String(string).replace(/[&<>"'\/]/g, function (s) { return entityMap[s]; })
};
/**
* find all filepath from A tag
*/
var getAllPaths = function () {
var paths = [];[].slice.call(document.querySelectorAll('a'))
.map(function (node) {
var href = node.href;
if (/#\/[^#]*?$/.test(href)) {
var path = href.replace(/^[^#]+#/, '');
if (paths.indexOf(path) <= 0) { paths.push(path); }
}
});
return paths
};
/**
* return file path
*/
var genFilePath = function (path) {
var basePath = window.$docsify.basePath;
var filePath = /\/$/.test(path) ? (path + "README.md") : (path + ".md");
filePath = basePath + filePath;
return filePath.replace(/\/+/g, '/')
};
/**
* generate index
*/
var genIndex = function (path, content) {
if ( content === void 0 ) content = '';
INDEXS[path] = { slug: '', title: '', body: '' };
var slug;
content
// remove PRE and TEMPLATE tag
.replace(/<template[^>]*?>[\s\S]+?<\/template>/g, '')
// find all html tag
.replace(/<(\w+)([^>]*?)>([\s\S]+?)<\//g, function (match, tag, attr, html) {
// remove all html tag
var text = html.replace(/<[^>]+>/g, '');
// tag is headline
if (/^h\d$/.test(tag)) {
// <h1 id="xxx"></h1>
var id = attr.match(/id="(\S+)"/)[1];
slug = ("#/" + path + "#" + id).replace(/\/+/, '/');
INDEXS[slug] = { slug: slug, title: text, body: '' };
} else {
if (!slug) { return }
// other html tag
if (!INDEXS[slug]) {
INDEXS[slug] = {};
} else {
if (INDEXS[slug].body && INDEXS[slug].body.length) {
INDEXS[slug].body += '\n' + text;
} else {
INDEXS[slug].body = text;
}
}
}
});
};
/**
* component
*/
var SearchComponent = function SearchComponent () {
if (this.rendered) { return }
this.style();
var el = document.createElement('div');
var aside = document.querySelector('aside');
el.classList.add('search');
aside.insertBefore(el, aside.children[0]);
this.render(el);
this.rendered = true;
this.bindEvent();
};
SearchComponent.prototype.style = function style () {
var code = "\n .sidebar {\n padding-top: 0;\n }\n\n .search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n }\n\n .search .results-panel {\n display: none;\n }\n\n .search .results-panel.show {\n display: block;\n }\n\n .search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 7px;\n line-height: 22px;\n font-size: 14px;\n }\n\n .search h2 {\n font-size: 17px;\n margin: 10px 0;\n }\n\n .search a {\n text-decoration: none;\n color: inherit;\n }\n\n .search .matching-post {\n border-bottom: 1px solid #eee;\n }\n\n .search .matching-post:last-child {\n border-bottom: 0;\n }\n\n .search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n }\n\n .search p.empty {\n text-align: center;\n }\n ";
var style = document.createElement('style');
style.innerHTML = code;
document.head.appendChild(style);
};
SearchComponent.prototype.render = function render (dom) {
dom.innerHTML = "<input type=\"search\" placeholder=\"" + (CONFIG.placeholder) + "\" /><div class=\"results-panel\"></div>";
};
SearchComponent.prototype.bindEvent = function bindEvent () {
var this$1 = this;
var search = document.querySelector('.search');
var input = search.querySelector('.search input');
var panel = search.querySelector('.results-panel');
search.addEventListener('click', function (e) { return e.target.tagName !== 'A' && e.stopPropagation(); });
input.addEventListener('input', function (e) {
var target = e.target;
if (target.value.trim() !== '') {
var matchingPosts = this$1.search(target.value);
var html = '';
matchingPosts.forEach(function (post, index) {
html += "\n <div class=\"matching-post\">\n <h2><a href=\"" + (post.url) + "\">" + (post.title) + "</a></h2>\n <p>" + (post.content) + "</p>\n </div>\n ";
});
if (panel.classList.contains('results-panel')) {
panel.classList.add('show');
panel.innerHTML = html || '<p class="empty">No Results!</p>';
}
} else {
if (panel.classList.contains('results-panel')) {
panel.classList.remove('show');
panel.innerHTML = '';
}
}
});
};
// From [weex website] https://weex-project.io/js/common.js
SearchComponent.prototype.search = function search (keywords) {
var matchingResults = [];
var data = Object.keys(INDEXS).map(function (key) { return INDEXS[key]; });
keywords = keywords.trim().split(/[\s\-\\\/]+/);
var loop = function ( i ) {
var post = data[i];
var isMatch = false;
var resultStr = '';
var postTitle = post.title && post.title.trim();
var postContent = post.body && post.body.trim();
var postUrl = post.slug || '';
if (postTitle !== '' && postContent !== '') {
keywords.forEach(function (keyword, i) {
var regEx = new RegExp(keyword, 'gi');
var indexTitle = -1;
var indexContent = -1;
indexTitle = postTitle.search(regEx);
indexContent = postContent.search(regEx);
if (indexTitle < 0 && indexContent < 0) {
isMatch = false;
} else {
isMatch = true;
if (indexContent < 0) { indexContent = 0; }
var start = 0;
var end = 0;
start = indexContent < 11 ? 0 : indexContent - 10;
end = start === 0 ? 70 : indexContent + keyword.length + 60;
if (end > postContent.length) { end = postContent.length; }
var matchContent = '...' +
postContent
.substring(start, end)
.replace(regEx, ("<em class=\"search-keyword\">" + keyword + "</em>")) +
'...';
resultStr += matchContent;
}
});
if (isMatch) {
var matchingPost = {
title: escapeHtml(postTitle),
content: resultStr,
url: postUrl
};
matchingResults.push(matchingPost);
}
}
};
for (var i = 0; i < data.length; i++) loop( i );
return matchingResults
};
var searchPlugin = function () {
var isAuto = CONFIG.paths === 'auto';
var isExpired = localStorage.getItem('docsify.search.expires') < Date.now();
INDEXS = JSON.parse(localStorage.getItem('docsify.search.index'));
if (isExpired) {
INDEXS = {};
} else if (!isAuto) {
return
}
var count = 0;
var paths = isAuto ? getAllPaths() : CONFIG.paths;
var len = paths.length;
var ref = window.Docsify.utils;
var load = ref.load;
var marked = ref.marked;
var slugify = ref.slugify;
var done = function () {
localStorage.setItem('docsify.search.expires', Date.now() + CONFIG.maxAge);
localStorage.setItem('docsify.search.index', JSON.stringify(INDEXS));
};
paths.forEach(function (path) {
if (INDEXS[path]) { return count++ }
load(genFilePath(path)).then(function (content) {
genIndex(path, marked(content));
slugify.clear();
count++;
if (len === count) { done(); }
});
});
};
var install = function () {
if (!window.Docsify || !window.Docsify.installed) {
console.error('[Docsify] Please load docsify.js first.');
return
}
window.$docsify.plugins = [].concat(window.$docsify.plugins, searchPlugin);
var userConfig = window.$docsify.search;
var isNil = window.Docsify.utils.isNil;
if (Array.isArray(userConfig)) {
CONFIG.paths = userConfig;
} else if (isObj(userConfig)) {
CONFIG.paths = Array.isArray(userConfig.paths) ? userConfig.paths : 'auto';
CONFIG.maxAge = isNil(userConfig.maxAge) ? CONFIG.maxAge : userConfig.maxAge;
CONFIG.placeholder = userConfig.placeholder || CONFIG.placeholder;
}
new SearchComponent();
};
var search = install();
return search;
}());
+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
File diff suppressed because one or more lines are too long
+6 -5
View File
@@ -1,6 +1,6 @@
{
"name": "docsify",
"version": "1.5.2",
"version": "2.2.1",
"description": "A magical documentation generator.",
"main": "lib/docsify.js",
"files": [
@@ -10,7 +10,8 @@
],
"scripts": {
"build": "rm -rf lib themes && node build/build.js && mkdir lib/themes && mkdir themes && node build/build-css.js",
"dev": "node app.js & nodemon -w src -e js,css --exec 'npm run build'",
"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"
},
"repository": {
@@ -35,9 +36,9 @@
"nodemon": "^1.11.0",
"postcss": "^5.2.6",
"postcss-salad": "^1.0.5",
"rollup": "^0.36.3",
"rollup-plugin-buble": "^0.14.0",
"rollup-plugin-commonjs": "^5.0.5",
"rollup": "^0.41.1",
"rollup-plugin-buble": "^0.15.0",
"rollup-plugin-commonjs": "^7.0.0",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-uglify": "^1.0.1",
"serve-static": "^1.11.1"
+63 -26
View File
@@ -7,10 +7,15 @@ import { isMobile } from './util'
export function scrollActiveSidebar () {
if (isMobile()) return
let hoveredOverSidebar = false
const anchors = document.querySelectorAll('.anchor')
const sidebar = document.querySelector('.sidebar')
const sidebarContainer = sidebar.querySelector('.sidebar-nav')
const sidebarHeight = sidebar.clientHeight
const nav = {}
const lis = document.querySelectorAll('.sidebar li')
let active = null
const lis = sidebar.querySelectorAll('li')
let active = sidebar.querySelector('li.active')
for (let i = 0, len = lis.length; i < len; i += 1) {
const li = lis[i]
@@ -25,27 +30,52 @@ export function scrollActiveSidebar () {
}
function highlight () {
const top = document.body.scrollTop
let last
for (let i = 0, len = anchors.length; i < len; i += 1) {
const node = anchors[i]
const bcr = node.getBoundingClientRect()
if (bcr.top < 10 && bcr.bottom > 10) {
const li = nav[node.getAttribute('data-id')]
if (!li || li === active) return
if (active) active.setAttribute('class', '')
li.setAttribute('class', 'active')
active = li
return
if (node.offsetTop > top) {
if (!last) last = node
break
} else {
last = node
}
}
if (!last) return
const li = nav[last.getAttribute('data-id')]
if (!li || li === active) return
if (active) active.classList.remove('active')
li.classList.add('active')
active = li
// scroll into view
// https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297
if (!hoveredOverSidebar && !sticky.noSticky) {
const currentPageOffset = 0
const currentActiveOffset = active.offsetTop + active.clientHeight + 40
const currentActiveIsInView = (
active.offsetTop >= sidebarContainer.scrollTop &&
currentActiveOffset <= sidebarContainer.scrollTop + sidebarHeight
)
const linkNotFurtherThanSidebarHeight = currentActiveOffset - currentPageOffset < sidebarHeight
const newScrollTop = currentActiveIsInView
? sidebarContainer.scrollTop
: linkNotFurtherThanSidebarHeight
? currentPageOffset
: currentActiveOffset - sidebarHeight
sidebar.scrollTop = newScrollTop
}
}
window.removeEventListener('scroll', highlight)
window.addEventListener('scroll', highlight)
highlight()
sidebar.addEventListener('mouseover', () => { hoveredOverSidebar = true })
sidebar.addEventListener('mouseleave', () => { hoveredOverSidebar = false })
}
export function scrollIntoView () {
@@ -53,7 +83,9 @@ export function scrollIntoView () {
if (!id || !id.length) return
const section = document.querySelector(decodeURIComponent(id[0]))
if (section) section.scrollIntoView()
if (section) setTimeout(() => section.scrollIntoView(), 0)
return section
}
/**
@@ -71,13 +103,13 @@ export function activeLink (dom, activeParent) {
.forEach(node => {
if (host.indexOf(node.href) === 0 && !target) {
activeParent
? node.parentNode.setAttribute('class', 'active')
: node.setAttribute('class', 'active')
? node.parentNode.classList.add('active')
: node.classList.add('active')
target = node
} else {
activeParent
? node.parentNode.removeAttribute('class')
: node.removeAttribute('class')
? node.parentNode.classList.remove('active')
: node.classList.remove('active')
}
})
@@ -95,24 +127,29 @@ export function bindToggle (dom) {
dom.addEventListener('click', () => body.classList.toggle('close'))
if (isMobile()) {
document.querySelector('aside')
.addEventListener('click', _ => body.classList.toggle('close'))
const sidebar = document.querySelector('.sidebar')
sidebar.addEventListener('click', () => {
body.classList.toggle('close')
setTimeout(() => activeLink(sidebar, true), 0)
})
}
}
export function scroll2Top () {
document.body.scrollTop = 0
export function scroll2Top (offset = 0) {
document.body.scrollTop = offset === true ? 0 : Number(offset)
}
export function sticky () {
const dom = document.querySelector('section.cover')
const coverHeight = dom.getBoundingClientRect().height
sticky.dom = sticky.dom || document.querySelector('section.cover')
const coverHeight = sticky.dom.getBoundingClientRect().height
return (function () {
if (window.pageYOffset >= coverHeight || dom.classList.contains('hidden')) {
if (window.pageYOffset >= coverHeight || sticky.dom.classList.contains('hidden')) {
document.body.classList.add('sticky')
sticky.noSticky = false
} else {
document.body.classList.remove('sticky')
sticky.noSticky = true
}
})()
}
+32 -23
View File
@@ -1,44 +1,53 @@
import { load, camel2kebab, isNil, getRoute, merge } from './util'
import * as utils from './util'
import { scrollIntoView, activeLink } from './event'
import * as render from './render'
const OPTIONS = merge({
const OPTIONS = utils.merge({
el: '#app',
repo: '',
maxLevel: 6,
subMaxLevel: 0,
sidebar: '',
sidebarToggle: false,
loadSidebar: null,
loadNavbar: null,
router: false,
homepage: 'README.md',
coverpage: '',
basePath: '',
auto2top: false
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-' + camel2kebab(prop))
OPTIONS[prop] = isNil(val) ? OPTIONS[prop] : (val || true)
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.sidebar) OPTIONS.sidebar = window[OPTIONS.sidebar]
if (OPTIONS.repo === true) OPTIONS.repo = ''
if (OPTIONS.name === true) OPTIONS.name = ''
}
// utils
window.$docsify = OPTIONS
window.Docsify = {
installed: true,
utils: utils.merge({}, utils)
}
// load options
render.init(OPTIONS)
render.init()
let cacheRoute = null
let cacheXhr = null
const mainRender = function (cb) {
const route = OPTIONS.basePath + getRoute()
const route = OPTIONS.basePath + utils.getRoute()
if (cacheRoute === route) return cb()
let basePath = cacheRoute = route
@@ -50,6 +59,7 @@ const mainRender = function (cb) {
}
let page
if (!route) {
page = OPTIONS.homepage || 'README.md'
} else if (/\/$/.test(route)) {
@@ -60,23 +70,22 @@ const mainRender = function (cb) {
// Render Cover page
if (OPTIONS.coverpage && page === OPTIONS.homepage) {
load(OPTIONS.coverpage).then(render.renderCover)
utils.load(OPTIONS.coverpage).then(render.renderCover)
}
cacheXhr && cacheXhr.abort && cacheXhr.abort()
// Render markdown file
cacheXhr = load(page, 'GET', render.renderLoading)
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) {
load(basePath + OPTIONS.loadSidebar)
.then(result => {
render.renderSidebar(result)
cb()
})
const renderSidebar = result => { render.renderSidebar(result); cb() }
utils.load(basePath + OPTIONS.loadSidebar).then(renderSidebar,
_ => utils.load(OPTIONS.loadSidebar).then(renderSidebar))
} else {
cb()
}
@@ -84,7 +93,8 @@ const mainRender = function (cb) {
// Render navbar
if (OPTIONS.loadNavbar) {
load(basePath + OPTIONS.loadNavbar).then(render.renderNavbar)
utils.load(basePath + OPTIONS.loadNavbar).then(render.renderNavbar,
_ => utils.load(OPTIONS.loadNavbar).then(render.renderNavbar))
}
}
@@ -95,16 +105,15 @@ const Docsify = function () {
mainRender(_ => {
scrollIntoView()
activeLink('nav')
;[].concat(window.$docsify.plugins).forEach(fn => fn && fn())
})
}
// Render app
render.renderApp(dom, replace)
main()
if (OPTIONS.router) {
if (!/^#\//.test(window.location.hash)) window.location.hash = '#/'
window.addEventListener('hashchange', main)
}
if (!/^#\//.test(window.location.hash)) window.location.hash = '#/'
window.addEventListener('hashchange', main)
}
export default Docsify()
+42
View File
@@ -0,0 +1,42 @@
// From https://github.com/egoist/vue-ga/blob/master/src/index.js
function appendScript () {
const script = document.createElement('script')
script.async = true
script.src = 'https://www.google-analytics.com/analytics.js'
document.body.appendChild(script)
}
function init (id) {
if (!window.ga) {
appendScript()
window.ga = window.ga || function () {
(window.ga.q = window.ga.q || []).push(arguments)
}
window.ga.l = Number(new Date())
window.ga('create', id, 'auto')
}
}
function collect () {
init(window.$docsify.ga)
window.ga('set', 'page', location.href)
window.ga('send', 'pageview')
}
const install = function () {
if (!window.Docsify || !window.Docsify.installed) {
console.error('[Docsify] Please load docsify.js first.')
return
}
if (!window.$docsify.ga) {
console.error('[Docsify] ga is required.')
return
}
collect()
window.$docsify.plugins = [].concat(window.$docsify.plugins, collect)
}
export default install()
+337
View File
@@ -0,0 +1,337 @@
let INDEXS = {}
const CONFIG = {
placeholder: 'Type to search',
paths: 'auto',
maxAge: 86400000 // 1 day
}
const isObj = function (obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
const escapeHtml = function (string) {
const entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;'
}
return String(string).replace(/[&<>"'\/]/g, s => entityMap[s])
}
/**
* find all filepath from A tag
*/
const getAllPaths = function () {
const paths = []
;[].slice.call(document.querySelectorAll('a'))
.map(node => {
const href = node.href
if (/#\/[^#]*?$/.test(href)) {
const path = href.replace(/^[^#]+#/, '')
if (paths.indexOf(path) <= 0) paths.push(path)
}
})
return paths
}
/**
* return file path
*/
const genFilePath = function (path) {
const basePath = window.$docsify.basePath
let filePath = /\/$/.test(path) ? `${path}README.md` : `${path}.md`
filePath = basePath + filePath
return filePath.replace(/\/+/g, '/')
}
/**
* generate index
*/
const genIndex = function (path, content = '') {
INDEXS[path] = { slug: '', title: '', body: '' }
let slug
content
// remove PRE and TEMPLATE tag
.replace(/<template[^>]*?>[\s\S]+?<\/template>/g, '')
// find all html tag
.replace(/<(\w+)([^>]*?)>([\s\S]+?)<\//g, (match, tag, attr, html) => {
// remove all html tag
const text = html.replace(/<[^>]+>/g, '')
// tag is headline
if (/^h\d$/.test(tag)) {
// <h1 id="xxx"></h1>
const id = attr.match(/id="(\S+)"/)[1]
slug = `#/${path}#${id}`.replace(/\/+/, '/')
INDEXS[slug] = { slug, title: text, body: '' }
} else {
if (!slug) return
// other html tag
if (!INDEXS[slug]) {
INDEXS[slug] = {}
} else {
if (INDEXS[slug].body && INDEXS[slug].body.length) {
INDEXS[slug].body += '\n' + text
} else {
INDEXS[slug].body = text
}
}
}
})
}
/**
* component
*/
class SearchComponent {
constructor () {
if (this.rendered) return
this.style()
const el = document.createElement('div')
const aside = document.querySelector('aside')
el.classList.add('search')
aside.insertBefore(el, aside.children[0])
this.render(el)
this.rendered = true
this.bindEvent()
}
style () {
const code = `
.sidebar {
padding-top: 0;
}
.search {
margin-bottom: 20px;
padding: 6px;
border-bottom: 1px solid #eee;
}
.search .results-panel {
display: none;
}
.search .results-panel.show {
display: block;
}
.search input {
outline: none;
border: none;
width: 100%;
padding: 7px;
line-height: 22px;
font-size: 14px;
}
.search h2 {
font-size: 17px;
margin: 10px 0;
}
.search a {
text-decoration: none;
color: inherit;
}
.search .matching-post {
border-bottom: 1px solid #eee;
}
.search .matching-post:last-child {
border-bottom: 0;
}
.search p {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.search p.empty {
text-align: center;
}
`
const style = document.createElement('style')
style.innerHTML = code
document.head.appendChild(style)
}
render (dom) {
dom.innerHTML = `<input type="search" placeholder="${CONFIG.placeholder}" /><div class="results-panel"></div>`
}
bindEvent () {
const search = document.querySelector('.search')
const input = search.querySelector('.search input')
const panel = search.querySelector('.results-panel')
search.addEventListener('click', e => e.target.tagName !== 'A' && e.stopPropagation())
input.addEventListener('input', e => {
const target = e.target
if (target.value.trim() !== '') {
const matchingPosts = this.search(target.value)
let html = ''
matchingPosts.forEach(function (post, index) {
html += `
<div class="matching-post">
<h2><a href="${post.url}">${post.title}</a></h2>
<p>${post.content}</p>
</div>
`
})
if (panel.classList.contains('results-panel')) {
panel.classList.add('show')
panel.innerHTML = html || '<p class="empty">No Results!</p>'
}
} else {
if (panel.classList.contains('results-panel')) {
panel.classList.remove('show')
panel.innerHTML = ''
}
}
})
}
// From [weex website] https://weex-project.io/js/common.js
search (keywords) {
const matchingResults = []
const data = Object.keys(INDEXS).map(key => INDEXS[key])
keywords = keywords.trim().split(/[\s\-\\\/]+/)
for (let i = 0; i < data.length; i++) {
const post = data[i]
let isMatch = false
let resultStr = ''
const postTitle = post.title && post.title.trim()
const postContent = post.body && post.body.trim()
const postUrl = post.slug || ''
if (postTitle !== '' && postContent !== '') {
keywords.forEach((keyword, i) => {
const regEx = new RegExp(keyword, 'gi')
let indexTitle = -1
let indexContent = -1
indexTitle = postTitle.search(regEx)
indexContent = postContent.search(regEx)
if (indexTitle < 0 && indexContent < 0) {
isMatch = false
} else {
isMatch = true
if (indexContent < 0) indexContent = 0
let start = 0
let end = 0
start = indexContent < 11 ? 0 : indexContent - 10
end = start === 0 ? 70 : indexContent + keyword.length + 60
if (end > postContent.length) end = postContent.length
const matchContent = '...' +
postContent
.substring(start, end)
.replace(regEx, `<em class="search-keyword">${keyword}</em>`) +
'...'
resultStr += matchContent
}
})
if (isMatch) {
const matchingPost = {
title: escapeHtml(postTitle),
content: resultStr,
url: postUrl
}
matchingResults.push(matchingPost)
}
}
}
return matchingResults
}
}
const searchPlugin = function () {
const isAuto = CONFIG.paths === 'auto'
const isExpired = localStorage.getItem('docsify.search.expires') < Date.now()
INDEXS = JSON.parse(localStorage.getItem('docsify.search.index'))
if (isExpired) {
INDEXS = {}
} else if (!isAuto) {
return
}
let count = 0
const paths = isAuto ? getAllPaths() : CONFIG.paths
const len = paths.length
const { load, marked, slugify } = window.Docsify.utils
const done = () => {
localStorage.setItem('docsify.search.expires', Date.now() + CONFIG.maxAge)
localStorage.setItem('docsify.search.index', JSON.stringify(INDEXS))
}
paths.forEach(path => {
if (INDEXS[path]) return count++
load(genFilePath(path)).then(content => {
genIndex(path, marked(content))
slugify.clear()
count++
if (len === count) done()
})
})
}
const install = function () {
if (!window.Docsify || !window.Docsify.installed) {
console.error('[Docsify] Please load docsify.js first.')
return
}
window.$docsify.plugins = [].concat(window.$docsify.plugins, searchPlugin)
const userConfig = window.$docsify.search
const isNil = window.Docsify.utils.isNil
if (Array.isArray(userConfig)) {
CONFIG.paths = userConfig
} else if (isObj(userConfig)) {
CONFIG.paths = Array.isArray(userConfig.paths) ? userConfig.paths : 'auto'
CONFIG.maxAge = isNil(userConfig.maxAge) ? CONFIG.maxAge : userConfig.maxAge
CONFIG.placeholder = userConfig.placeholder || CONFIG.placeholder
}
new SearchComponent()
}
export default install()
+30
View File
@@ -0,0 +1,30 @@
import { load } from './util'
function replaceVar (block) {
block.innerHTML = block.innerHTML.replace(/var\(\s*--theme-color.*?\)/g, $docsify.themeColor)
}
export function cssVars () {
// variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) return
const styleBlocks = document.querySelectorAll('style:not(.inserted),link')
;[].forEach.call(styleBlocks, block => {
if (block.nodeName === 'STYLE') {
replaceVar(block)
} else if (block.nodeName === 'LINK') {
const href = block.getAttribute('href')
if (!/\.css$/.test(href)) return
load(href).then(res => {
const style = document.createElement('style')
style.innerHTML = res
document.head.appendChild(style)
replaceVar(style)
})
}
})
}
+85 -46
View File
@@ -1,10 +1,10 @@
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, slugify, merge } from './util'
import * as event from './event'
import * as polyfill from './polyfill'
import { genTree, getRoute, isMobile, slugify, merge, emojify } from './util'
let OPTIONS = {}
let markdown = marked
let toc = []
const CACHE = {}
@@ -19,11 +19,8 @@ const renderTo = function (dom, content) {
/**
* init render
* @param {Object} options
*/
export function init (options) {
OPTIONS = options
export function init () {
const renderer = new marked.Renderer()
/**
* render anchor tag
@@ -33,10 +30,7 @@ export function init (options) {
const slug = slugify(text)
let route = ''
if (OPTIONS.router) {
route = `#/${getRoute()}`
}
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}>`
@@ -44,23 +38,40 @@ export function init (options) {
// 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>`
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${hl}</code></pre>`
}
renderer.link = function (href, title, text) {
if (OPTIONS.router && !/:/.test(href)) {
href = `#/${href}`.replace(/\/\//g, '/')
if (!/:/.test(href)) {
href = `#/${href}`.replace(/\/+/g, '/')
}
return `<a href="${href}" title="${title || ''}">${text}</a>`
}
renderer.paragraph = function (text) {
if (/^!&gt;/.test(text)) {
return tpl.helper('tip', text)
} else if (/^\?&gt;/.test(text)) {
return tpl.helper('warn', text)
}
return `<p>${text}</p>`
}
if (typeof OPTIONS.markdown === 'function') {
if (typeof $docsify.markdown === 'function') {
markdown.setOptions({ renderer })
markdown = OPTIONS.markdown.call(this, markdown)
markdown = $docsify.markdown.call(this, markdown)
} else {
markdown.setOptions(merge({ renderer }, OPTIONS.markdown))
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
}
}
@@ -69,21 +80,35 @@ export function init (options) {
*/
export function renderApp (dom, replace) {
const nav = document.querySelector('nav') || document.createElement('nav')
const body = document.body
const head = document.head
if (!OPTIONS.repo) nav.classList.add('no-badge')
if (!$docsify.repo) nav.classList.add('no-badge')
dom[replace ? 'outerHTML' : 'innerHTML'] = tpl.corner(OPTIONS.repo) +
(OPTIONS.coverpage ? tpl.cover() : '') +
tpl.main(OPTIONS.sidebarToggle ? tpl.toggle() : '')
document.body.insertBefore(nav, document.body.children[0])
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
bindToggle('button.sidebar-toggle')
event.bindToggle('button.sidebar-toggle')
// bind sticky effect
if (OPTIONS.coverpage) {
!isMobile() && window.addEventListener('scroll', sticky)
if ($docsify.coverpage) {
!isMobile() && window.addEventListener('scroll', event.sticky)
} else {
document.body.classList.add('sticky')
body.classList.add('sticky')
}
}
@@ -92,13 +117,24 @@ export function renderApp (dom, replace) {
*/
export function renderArticle (content) {
renderTo('article', content ? markdown(content) : 'not found')
if (!OPTIONS.sidebar && !OPTIONS.loadSidebar) renderSidebar()
if (!$docsify.loadSidebar) renderSidebar()
if (content && typeof Vue !== 'undefined' && typeof Vuep !== 'undefined') {
const vm = new Vue({ el: 'main' }) // eslint-disable-line
vm.$nextTick(_ => scrollActiveSidebar())
if (content && 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 (OPTIONS.auto2top) scroll2Top()
if ($docsify.auto2top) setTimeout(() => event.scroll2Top($docsify.auto2top), 0)
}
/**
@@ -109,7 +145,7 @@ export function renderNavbar (content) {
CACHE.navbar = content
if (content) renderTo('nav', markdown(content))
activeLink('nav')
event.activeLink('nav')
}
/**
@@ -120,23 +156,23 @@ export function renderSidebar (content) {
if (content) {
html = markdown(content)
} else if (OPTIONS.sidebar) {
html = tpl.tree(OPTIONS.sidebar, '<ul>')
// find url tag
html = html.match(/<ul[^>]*>([\s\S]+)<\/ul>/g)[0]
} else {
html = tpl.tree(genTree(toc, OPTIONS.maxLevel), '<ul>')
html = tpl.tree(genTree(toc, $docsify.maxLevel), '<ul>')
}
renderTo('aside.sidebar', html)
const target = activeLink('aside.sidebar', true)
if (content) renderSubSidebar(target)
renderTo('.sidebar-nav', html)
const target = event.activeLink('.sidebar-nav', true)
if (target) renderSubSidebar(target)
toc = []
scrollActiveSidebar()
event.scrollActiveSidebar()
}
export function renderSubSidebar (target) {
if (!OPTIONS.subMaxLevel) return
target.parentNode.innerHTML += tpl.tree(genTree(toc, OPTIONS.subMaxLevel), '<ul>')
if (!$docsify.subMaxLevel) return
target.parentNode.innerHTML += tpl.tree(genTree(toc, $docsify.subMaxLevel), '<ul>')
}
/**
@@ -149,18 +185,21 @@ export function renderCover (content) {
return
}
renderCover.dom.classList.add('show')
if (renderCover.rendered) return
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>$')
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]
coverEl.style.background = match[1] + (match[3] || '')
} else {
coverEl.classList.add('has-mask')
coverEl.style.backgroundImage = `url(${match[1]})`
@@ -171,7 +210,7 @@ export function renderCover (content) {
renderTo('.cover-main', html)
renderCover.rendered = true
sticky()
event.sticky()
}
/**
+5 -3
View File
@@ -72,9 +72,11 @@ section.cover {
.cover-main p:last-child a {
border-radius: 2em;
border: 1px solid $color-primary;
border-width: 1px;
border-style: solid;
border-color: var(--theme-color, $color-primary);
box-sizing: border-box;
color: $color-primary;
color: var(--theme-color, $color-primary);
font-size: 1.05em;
letter-spacing: 0.1em;
padding: 0.75em 2em;
@@ -85,7 +87,7 @@ section.cover {
&:last-child {
margin-right: 0;
background-color: $color-primary;
background-color: var(--theme-color, $color-primary);
color: #fff;
&:hover {
+101 -8
View File
@@ -7,8 +7,13 @@
-webkit-font-smoothing: antialiased;
}
.emoji {
height: 1.2em;
vertical-align: middle;
}
.progress {
background-color: $color-primary;
background-color: var(--theme-color, $color-primary);
height: 2px;
left: 0px;
position: fixed;
@@ -19,6 +24,15 @@
z-index: 999999;
}
.search a:hover {
color: var(--theme-color, $color-primary);
}
.search .search-keyword {
color: var(--theme-color, $color-primary);
font-style: normal;
}
html, body {
height: 100%;
}
@@ -81,12 +95,12 @@ nav {
transition: color .3s;
&:hover {
color: $color-primary;
color: var(--theme-color, $color-primary);
}
&.active {
color: $color-primary;
border-bottom: 2px solid $color-primary;
color: var(--theme-color, $color-primary);
border-bottom: 2px solid var(--theme-color, $color-primary);
}
}
@@ -96,8 +110,11 @@ nav {
display: inline-block;
ul {
border-radius: 2px;
background-color: rgba($color-bg, .6);
border: 1px solid $color-primary;
border-width: 1px;
border-style: solid;
border-color: var(--theme-color, $color-primary);
opacity: 0;
overflow: hidden;
padding: 0;
@@ -154,12 +171,13 @@ nav {
color: $color-bg;
height: 80px;
width: 80px;
fill: $color-primary;
fill: var(--theme-color, $color-primary);
}
}
/* main */
main {
display: block;
size: 100vw 100%;
position: relative;
}
@@ -180,13 +198,25 @@ main {
/* sidebar */
.sidebar {
border-right: 1px solid rgba(0, 0, 0, .07);
overflow-y: auto;
padding: 40px 0;
position: absolute 0 * 0 0;
transition: transform 250ms ease-out;
width: $sidebar-width;
z-index: 20;
border-right: 1px solid rgba(0, 0, 0, .07);
> h1 {
text-align: center;
margin: 0 auto 1em;
font-size: 1.5em;
font-weight: 300;
a {
color: inherit;
text-decoration: none;
}
}
ul {
margin: 0;
@@ -205,6 +235,23 @@ main {
ul li ul {
padding-left: 20px;
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 4px;
}
&:hover::-webkit-scrollbar-thumb {
background: rgba(136, 136, 136, 0.4);
}
&:hover::-webkit-scrollbar-track {
background: rgba(136, 136, 136, 0.1);
}
}
/* sidebar toggle */
@@ -227,7 +274,7 @@ main {
}
span {
background-color: $color-primary;
background-color: var(--theme-color, $color-primary);
display: block;
size: 16px 2px;
margin-bottom: 4px;
@@ -264,6 +311,12 @@ body.sticky {
}
}
.markdown-section hr {
border: none;
margin: 2em 0;
border-bottom: 1px solid #eee;
}
.markdown-section table {
display: block;
width: 100%;
@@ -291,6 +344,46 @@ body.sticky {
}
}
.markdown-section p.tip {
padding: 12px 24px 12px 30px;
margin: 2em 0;
border-left: 4px solid #f66;
background-color: #f8f8f8;
position: relative;
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
&:before {
position: absolute;
top: 14px;
left: -12px;
background-color: #f66;
color: $color-bg;
content: "!";
size: 20px;
border-radius: 100%;
text-align: center;
line-height: 20px;
font-weight: bold;
font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
font-size: 14px;
}
code {
background-color: #efefef;
}
em {
color: $color-text;
}
}
.markdown-section p.warn {
padding: 1em;
background: rgba($color-primary, 0.1);
border-radius: 2px;
}
body.close {
.sidebar {
transform: translateX(-$sidebar-width);
+3 -15
View File
@@ -28,7 +28,7 @@ $sidebar-width: 16em;
padding: 0;
}
.active {
li.active {
background-color: #eee;
a {
@@ -47,7 +47,7 @@ $sidebar-width: 16em;
}
.markdown-section a {
color: $color-primary;
color: var(--theme-color, $color-primary);
font-weight: 400;
}
@@ -110,7 +110,7 @@ $sidebar-width: 16em;
.markdown-section blockquote {
color: #858585;
border-left: 4px solid $color-primary;
border-left: 4px solid var(--theme-color, $color-primary);
margin: 2em 0;
padding-left: 20px;
}
@@ -238,15 +238,3 @@ code .token {
-webkit-font-smoothing: initial;
-moz-osx-font-smoothing: initial;
}
.content span.light {
color: #7f8c8d;
}
.content span.info {
display: inline-block;
font-size: 0.85em;
margin-left: 20px;
vertical-align: middle;
width: 280px;
}
+7 -18
View File
@@ -22,11 +22,12 @@ body {
}
ul li a {
color: #7f8c8d;
color: #505d6b;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-decoration: none;
font-size: 14px;
&:hover {
text-decoration: underline;
@@ -38,7 +39,7 @@ body {
}
ul li.active>a {
color: $color-primary;
color: var(--theme-color, $color-primary);
font-weight: 600;
}
}
@@ -54,7 +55,7 @@ body {
}
.markdown-section a {
color: $color-primary;
color: var(--theme-color, $color-primary);
font-weight: 600;
}
@@ -107,7 +108,7 @@ body {
.markdown-section blockquote {
color: #858585;
border-left: 4px solid $color-primary;
border-left: 4px solid var(--theme-color, $color-primary);
margin: 2em 0;
padding-left: 20px;
}
@@ -179,7 +180,7 @@ body {
}
.token.string {
color: $color-primary;
color: var(--theme-color, $color-primary);
}
.token.selector {
@@ -201,7 +202,7 @@ body {
.token.control,
.token.directive,
.token.unit {
color: $color-primary;
color: var(--theme-color, $color-primary);
}
.token.keyword {
@@ -285,15 +286,3 @@ pre::after {
text-align: right;
top: 0;
}
.content span.light {
color: #7f8c8d;
}
.content span.info {
display: inline-block;
font-size: 0.85em;
margin-left: 20px;
vertical-align: middle;
width: 280px;
}
+9 -2
View File
@@ -22,8 +22,8 @@ export function corner (data) {
/**
* Render main content
*/
export function main (tpl) {
const aside = `${tpl}<aside class="sidebar"></aside>`
export function main () {
const aside = `${toggle()}<aside class="sidebar"><div class="sidebar-nav"></div></aside>`
return (isMobile() ? `${aside}<main>` : `<main>${aside}`) +
`<section class="content">
@@ -72,3 +72,10 @@ export function tree (toc, tpl = '') {
return tpl
}
export function helper (className, content) {
return `<p class="${className}">${content.slice(5).trim()}</p>`
}
export function theme (color) {
return `<style>:root{--theme-color: ${color};}</style>`
}
+9 -1
View File
@@ -89,7 +89,7 @@ export function getRoute () {
const loc = window.location
if (cacheHash === loc.hash && !isNil(cacheRoute)) return cacheRoute
let route = loc.hash.match(/^#\/([^#]+)/)
let route = loc.hash.replace(/%23/g, '#').match(/^#\/([^#]+)/)
if (route && route.length === 2) {
route = route[1]
@@ -157,3 +157,11 @@ export const merge = Object.assign || function (to) {
return to
}
export function emojify (text) {
return text
.replace(/<(pre|template)[^>]*?>([\s\S]+)<\/(pre|template)>/g, match => match.replace(/:/g, '__colon__'))
.replace(/:(\w*?):/ig, '<img class="emoji" src="https://assets-cdn.github.com/images/icons/emoji/$1.png" alt="$1" />')
.replace(/__colon__/g, ':')
}
+3434
View File
File diff suppressed because it is too large Load Diff