mirror of
https://gitee.com/docsifyjs/docsify.git
synced 2024-11-29 10:38:48 +08:00
Ensure code format (#2138)
* chore: add missing Vue support for Vercel builds * refactor: move some functions and module-level state into classes as private methods and properties to start to encapsulate Docsify Also some small tweaks: - move initGlobalAPI out of Docsify.js to start to encapsulate Docsify - move ajax to utils folder - fix some type definitions and improve content in some JSDoc comments - use concise class field syntax - consolidate duplicate docsify-ignore comment removal code This handles a task in [Simplify and modernize Docsify](https://github.com/docsifyjs/docsify/issues/2104), as well as works towards [Encapsulating Docsify](https://github.com/docsifyjs/docsify/issues/2135). * chore: add prettier code format check to our lint script, and add a prettier script for manually formatting the whole code base * chore: update issue/pr templates * chore: apply our format to the whole code base --------- Co-authored-by: Koy <koy@ko8e24.top> Co-authored-by: i544693 <369491420@qq.com>
This commit is contained in:
parent
6464cf533d
commit
7bbf13d9bd
55
.github/ISSUE_TEMPLATE/bug_report.md
vendored
55
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,44 +1,53 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
about: Create a report to help us fix an issue
|
||||
---
|
||||
<!-- Please don't delete this template or we'll close your issue -->
|
||||
<!-- Please use English language -->
|
||||
<!-- Before creating an issue please make sure you are using the latest version of Docsify. -->
|
||||
<!-- Please ask questions on StackOverflow: https://stackoverflow.com/questions/ask?tags=docsify -->
|
||||
<!-- Please ask questions on Discord: https://discord.gg/3NwKFyR -->
|
||||
|
||||
<!--
|
||||
Please don't delete this template or we'll close your issue.
|
||||
Please write in English.
|
||||
Before creating an issue please make sure you are using the latest version of Docsify.
|
||||
Please ask questions on StackOverflow (👉 https://stackoverflow.com/questions/ask?tags=docsify)
|
||||
or in our Discord chat server (👉 https://discord.gg/docsify).
|
||||
-->
|
||||
|
||||
## Bug Report
|
||||
|
||||
#### Steps to reproduce
|
||||
|
||||
<!-- List the steps needed to reproduce here. -->
|
||||
|
||||
#### Current behaviour
|
||||
|
||||
#### What is current behaviour
|
||||
|
||||
|
||||
|
||||
#### What is the expected behaviour
|
||||
<!-- Describe the current behavior here. -->
|
||||
|
||||
#### Expected behaviour
|
||||
|
||||
<!-- Describe the expected behavior here. -->
|
||||
|
||||
#### Other relevant information
|
||||
|
||||
<!-- (Update "[ ]" to "[x]" to check a box) -->
|
||||
- [ ] Bug does still occur when all/other plugins are disabled?
|
||||
- Docsify version:
|
||||
|
||||
- Your OS:
|
||||
- Node.js version:
|
||||
- npm/yarn version:
|
||||
- Browser version:
|
||||
- Docsify version:
|
||||
- Docsify plugins:
|
||||
<!-- (Change "[ ]" to "[x]" to check a box.) -->
|
||||
|
||||
<!-- Love docsify? Please consider supporting our collective:
|
||||
👉 https://opencollective.com/docsify/donate -->
|
||||
- [ ] Bug still occurs when all/other plugins are disabled?
|
||||
|
||||
#### Please create a reproducible sandbox
|
||||
- Docsify plugins (if the bug happens when plugins enabled, please try to isolate the issue):
|
||||
|
||||
<!-- Please provide the following information if relevant to the issue.
|
||||
|
||||
- Your OS:
|
||||
- Node.js version:
|
||||
- npm/yarn version:
|
||||
- Browser version:
|
||||
|
||||
-->
|
||||
|
||||
<!-- Love docsify and want to help us advance Docsify or to spend time supporting your case? Please consider supporting our collective:
|
||||
👉 https://opencollective.com/docsify/donate -->
|
||||
|
||||
#### Please create a reproducible sandbox
|
||||
|
||||
[![Edit 307qqv236](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/307qqv236)
|
||||
|
||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord - the community chat
|
||||
url: https://discord.gg/3NwKFyR
|
||||
about: Join Discord community and chat about Docsify
|
||||
url: https://discord.gg/docsify
|
||||
about: Join the Discord community and chat about Docsify
|
||||
|
31
.github/ISSUE_TEMPLATE/feature_request.md
vendored
31
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,25 +1,32 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
<!-- Please don't delete this template or we'll close your issue -->
|
||||
<!-- Please use English language -->
|
||||
<!-- Before creating an issue please make sure you are using the latest version of Docsify. -->
|
||||
<!-- Please ask questions on StackOverflow 👉 https://stackoverflow.com/questions/ask?tags=docsify -->
|
||||
|
||||
<!--
|
||||
Please don't delete this template or we'll close your issue.
|
||||
Please write in English.
|
||||
Before creating an issue please make sure you are using the latest version of Docsify.
|
||||
Please ask questions on StackOverflow (👉 https://stackoverflow.com/questions/ask?tags=docsify)
|
||||
or in our Discord chat server (👉 https://discord.gg/docsify).
|
||||
-->
|
||||
|
||||
## Feature request
|
||||
|
||||
#### What problem does this feature solve?
|
||||
#### Problem or desire
|
||||
|
||||
<!-- Describe what problem this feature will solve. -->
|
||||
|
||||
#### Proposal
|
||||
|
||||
#### What does the proposed API look like?
|
||||
<!-- What does the proposed feature look like? -->
|
||||
|
||||
#### Implementation
|
||||
|
||||
<!--
|
||||
How should the feature be implemented in your opinion?
|
||||
|
||||
#### How should this be implemented in your opinion?
|
||||
|
||||
|
||||
|
||||
#### Are you willing to work on this yourself?
|
||||
We have limited free time. Please consider contributing, or donating to our
|
||||
OpenCollective (👉 https://opencollective.com/docsify/donate), to help get this
|
||||
feature added and to advance Docsify.
|
||||
-->
|
||||
|
54
.github/PULL_REQUEST_TEMPLATE.md
vendored
54
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,31 +1,25 @@
|
||||
<!--
|
||||
PULL REQUEST TEMPLATE
|
||||
---
|
||||
Please use English language
|
||||
Please don't delete this template
|
||||
---
|
||||
Update "[ ]" to "[x]" to check a box in any list below.
|
||||
---
|
||||
To avoid wasting your time, it's best to open a **feature request issue** first and wait for approval before working on it.
|
||||
Please write in English.
|
||||
Please follow the template, all sections are required.
|
||||
Consider opening a feature request first to get your change idea approved.
|
||||
-->
|
||||
|
||||
## **Summary**
|
||||
## Summary
|
||||
|
||||
<!--
|
||||
THIS IS REQUIRED! Please describe what the change does and why it should be merged.
|
||||
Describe what the change does and why it should be merged.
|
||||
Provide **before/after** screenshots for any UI changes.
|
||||
-->
|
||||
|
||||
<!--
|
||||
If changing the UI in any way, please provide the a **before/after** screenshot:
|
||||
-->
|
||||
## Related issue, if any:
|
||||
|
||||
## **What kind of change does this PR introduce?**
|
||||
<!-- Paste issue's link or number hashtag here. -->
|
||||
|
||||
## What kind of change does this PR introduce?
|
||||
|
||||
<!--
|
||||
Copy/paste one of the following options:
|
||||
-->
|
||||
Copy/paste any of the relevant following options:
|
||||
|
||||
<!--
|
||||
Bugfix
|
||||
Feature
|
||||
Code style update
|
||||
@ -34,29 +28,27 @@
|
||||
Build-related changes
|
||||
Repo settings
|
||||
Other
|
||||
|
||||
If you choose Other, describe it.
|
||||
-->
|
||||
|
||||
<!--
|
||||
If you chose Other, please describe.
|
||||
-->
|
||||
## For any code change,
|
||||
|
||||
## **For any code change,**
|
||||
<!-- (Change "[ ]" to "[x]" to check a box.) -->
|
||||
|
||||
- [ ] Related documentation has been updated if needed
|
||||
- [ ] Related tests have been updated or tests have been added
|
||||
- [ ] Related documentation has been updated, if needed
|
||||
- [ ] Related tests have been added or updated, if needed
|
||||
|
||||
## **Does this PR introduce a breaking change?** (check one)
|
||||
## Does this PR introduce a breaking change?
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
<!-- (pick one) -->
|
||||
|
||||
If yes, please describe the impact and migration path for existing applications:
|
||||
Yes
|
||||
No
|
||||
|
||||
## **Related issue, if any:**
|
||||
<!-- If yes, describe the impact and migration path for existing applications. -->
|
||||
|
||||
<!-- Paste issue's link or number hashtag here. -->
|
||||
|
||||
## **Tested in the following browsers:**
|
||||
## Tested in the following browsers:
|
||||
|
||||
- [ ] Chrome
|
||||
- [ ] Firefox
|
||||
|
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@ -6,12 +6,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
directory: '/' # Location of package manifests
|
||||
open-pull-requests-limit: 10
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
interval: 'weekly'
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
open-pull-requests-limit: 10
|
||||
schedule:
|
||||
interval: monthly
|
||||
|
@ -7,4 +7,4 @@ ports:
|
||||
onOpen: ignore
|
||||
vscode:
|
||||
extensions:
|
||||
- sysoev.language-stylus@1.11.0:xX39oruAJ5UQzTNVRdbBaQ==
|
||||
- sysoev.language-stylus@1.11.0:xX39oruAJ5UQzTNVRdbBaQ==
|
||||
|
6
.prettierignore
Normal file
6
.prettierignore
Normal file
@ -0,0 +1,6 @@
|
||||
CHANGELOG.md
|
||||
HISTORY.md
|
||||
lib/
|
||||
themes/
|
||||
_playwright-*/
|
||||
emoji-data.*
|
@ -1,3 +1,4 @@
|
||||
<!-- Historical history file (do not edit). -->
|
||||
|
||||
<a name="3.7.3"></a>
|
||||
## [3.7.3](https://github.com/QingWei-Li/docsify/compare/v3.7.2...v3.7.3) (2017-05-22)
|
||||
|
10
README.md
10
README.md
@ -27,7 +27,7 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Docsify turns one or more Markdown files into a Website, with no build process required.
|
||||
Docsify turns one or more Markdown files into a Website, with no build process required.
|
||||
|
||||
## Features
|
||||
|
||||
@ -57,14 +57,6 @@ A large collection of showcase projects are included in [awesome-docsify](https:
|
||||
- [Awesome docsify](https://github.com/docsifyjs/awesome-docsify)
|
||||
- [Community chat](https://discord.gg/3NwKFyR)
|
||||
|
||||
## Similar Projects
|
||||
|
||||
| Project | Description |
|
||||
| ------------------------------------------------ | ---------------------------------------- |
|
||||
| [Docusaurus](https://docusaurus.io) | Docusaurus makes it easy to maintain Open Source documentation websites |
|
||||
| [MkDocs](https://www.mkdocs.org) | Project documentation with Markdown |
|
||||
| [VitePress](https://vitepress.dev/) | Vite & Vue Powered Static Site Generator |
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
||||
|
@ -6,7 +6,7 @@ If you believe you have found a security vulnerability in docsify, please report
|
||||
|
||||
**Please do not report security vulnerabilities through our public GitHub issues.**
|
||||
|
||||
Send email via :email: maintainers@docsifyjs.org to us.
|
||||
Send email to us via :email: maintainers@docsifyjs.org.
|
||||
|
||||
Please include as much of the following information as possible to help us better understand the possible issue:
|
||||
|
||||
|
134
build/build.js
134
build/build.js
@ -32,83 +32,89 @@ async function build(opts) {
|
||||
nodeResolve(),
|
||||
replace({
|
||||
__VERSION__: version,
|
||||
})
|
||||
}),
|
||||
],
|
||||
onwarn(message) {
|
||||
if (message.code === 'UNRESOLVED_IMPORT') {
|
||||
throw new Error(
|
||||
`Could not resolve module ` +
|
||||
message.source +
|
||||
`. Try running 'npm install' or using rollup's 'external' option if this is an external dependency. ` +
|
||||
`Module ${message.source} is imported in ${message.importer}`
|
||||
)
|
||||
message.source +
|
||||
`. Try running 'npm install' or using rollup's 'external' option if this is an external dependency. ` +
|
||||
`Module ${message.source} is imported in ${message.importer}`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
.then(bundle => {
|
||||
const dest = 'lib/' + (opts.output || opts.input)
|
||||
const dest = 'lib/' + (opts.output || opts.input);
|
||||
|
||||
console.log(dest)
|
||||
console.log(dest);
|
||||
return bundle.write({
|
||||
format: 'iife',
|
||||
output: opts.globalName ? {name: opts.globalName} : {},
|
||||
output: opts.globalName ? { name: opts.globalName } : {},
|
||||
file: dest,
|
||||
strict: false
|
||||
})
|
||||
})
|
||||
strict: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function buildCore() {
|
||||
const promises = []
|
||||
const promises = [];
|
||||
|
||||
promises.push(build({
|
||||
input: 'src/core/index.js',
|
||||
output: 'docsify.js',
|
||||
}))
|
||||
promises.push(
|
||||
build({
|
||||
input: 'src/core/index.js',
|
||||
output: 'docsify.js',
|
||||
})
|
||||
);
|
||||
|
||||
if (isProd) {
|
||||
promises.push(build({
|
||||
input: 'src/core/index.js',
|
||||
output: 'docsify.min.js',
|
||||
plugins: [uglify()]
|
||||
}))
|
||||
promises.push(
|
||||
build({
|
||||
input: 'src/core/index.js',
|
||||
output: 'docsify.min.js',
|
||||
plugins: [uglify()],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async function buildAllPlugin() {
|
||||
const plugins = [
|
||||
{name: 'search', input: 'search/index.js'},
|
||||
{name: 'ga', input: 'ga.js'},
|
||||
{name: 'gtag', input: 'gtag.js'},
|
||||
{name: 'matomo', input: 'matomo.js'},
|
||||
{name: 'emoji', input: 'emoji.js'},
|
||||
{name: 'external-script', input: 'external-script.js'},
|
||||
{name: 'front-matter', input: 'front-matter/index.js'},
|
||||
{name: 'zoom-image', input: 'zoom-image.js'},
|
||||
{name: 'disqus', input: 'disqus.js'},
|
||||
{name: 'gitalk', input: 'gitalk.js'}
|
||||
]
|
||||
{ name: 'search', input: 'search/index.js' },
|
||||
{ name: 'ga', input: 'ga.js' },
|
||||
{ name: 'gtag', input: 'gtag.js' },
|
||||
{ name: 'matomo', input: 'matomo.js' },
|
||||
{ name: 'emoji', input: 'emoji.js' },
|
||||
{ name: 'external-script', input: 'external-script.js' },
|
||||
{ name: 'front-matter', input: 'front-matter/index.js' },
|
||||
{ name: 'zoom-image', input: 'zoom-image.js' },
|
||||
{ name: 'disqus', input: 'disqus.js' },
|
||||
{ name: 'gitalk', input: 'gitalk.js' },
|
||||
];
|
||||
|
||||
const promises = plugins.map(item => {
|
||||
return build({
|
||||
input: 'src/plugins/' + item.input,
|
||||
output: 'plugins/' + item.name + '.js'
|
||||
})
|
||||
})
|
||||
output: 'plugins/' + item.name + '.js',
|
||||
});
|
||||
});
|
||||
|
||||
if (isProd) {
|
||||
plugins.forEach(item => {
|
||||
promises.push(build({
|
||||
input: 'src/plugins/' + item.input,
|
||||
output: 'plugins/' + item.name + '.min.js',
|
||||
plugins: [uglify()]
|
||||
}))
|
||||
})
|
||||
promises.push(
|
||||
build({
|
||||
input: 'src/plugins/' + item.input,
|
||||
output: 'plugins/' + item.name + '.min.js',
|
||||
plugins: [uglify()],
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@ -118,41 +124,37 @@ async function main() {
|
||||
atomic: true,
|
||||
awaitWriteFinish: {
|
||||
stabilityThreshold: 1000,
|
||||
pollInterval: 100
|
||||
}
|
||||
pollInterval: 100,
|
||||
},
|
||||
})
|
||||
.on('change', p => {
|
||||
console.log('[watch] ', p)
|
||||
const dirs = p.split(path.sep)
|
||||
console.log('[watch] ', p);
|
||||
const dirs = p.split(path.sep);
|
||||
if (dirs[1] === 'core') {
|
||||
buildCore()
|
||||
buildCore();
|
||||
} else if (dirs[2]) {
|
||||
const name = path.basename(dirs[2], '.js')
|
||||
const name = path.basename(dirs[2], '.js');
|
||||
const input = `src/plugins/${name}${
|
||||
/\.js/.test(dirs[2]) ? '' : '/index'
|
||||
}.js`
|
||||
}.js`;
|
||||
|
||||
build({
|
||||
input,
|
||||
output: 'plugins/' + name + '.js'
|
||||
})
|
||||
output: 'plugins/' + name + '.js',
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('ready', () => {
|
||||
console.log('[start]')
|
||||
buildCore()
|
||||
buildAllPlugin()
|
||||
})
|
||||
console.log('[start]');
|
||||
buildCore();
|
||||
buildAllPlugin();
|
||||
});
|
||||
} else {
|
||||
await Promise.all([
|
||||
buildCore(),
|
||||
buildAllPlugin()
|
||||
])
|
||||
await Promise.all([buildCore(), buildAllPlugin()]);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
main().catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
|
77
build/css.js
77
build/css.js
@ -1,47 +1,48 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import {spawn} from 'child_process'
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
const relative = path => new URL(path, import.meta.url);
|
||||
const args = process.argv.slice(2)
|
||||
const args = process.argv.slice(2);
|
||||
fs.readdir(relative('../src/themes'), (err, files) => {
|
||||
if (err) {
|
||||
console.error('err', err)
|
||||
process.exit(1)
|
||||
}
|
||||
files.map(async (file) => {
|
||||
if (/\.styl/g.test(file)) {
|
||||
const stylusBin = ['node_modules', 'stylus', 'bin', 'stylus'].join(path.sep)
|
||||
let cmdargs = [
|
||||
stylusBin,
|
||||
`src/themes/${file}`,
|
||||
'-u',
|
||||
'autoprefixer-stylus'
|
||||
]
|
||||
cmdargs = [...cmdargs, ...args]
|
||||
if (err) {
|
||||
console.error('err', err);
|
||||
process.exit(1);
|
||||
}
|
||||
files.map(async file => {
|
||||
if (/\.styl/g.test(file)) {
|
||||
const stylusBin = ['node_modules', 'stylus', 'bin', 'stylus'].join(
|
||||
path.sep
|
||||
);
|
||||
let cmdargs = [
|
||||
stylusBin,
|
||||
`src/themes/${file}`,
|
||||
'-u',
|
||||
'autoprefixer-stylus',
|
||||
];
|
||||
cmdargs = [...cmdargs, ...args];
|
||||
|
||||
const stylusCMD = spawn('node', cmdargs, { shell: true })
|
||||
const stylusCMD = spawn('node', cmdargs, { shell: true });
|
||||
|
||||
stylusCMD.stdout.on('data', (data) => {
|
||||
console.log(`[Stylus Build ] stdout: ${data}`);
|
||||
});
|
||||
stylusCMD.stdout.on('data', data => {
|
||||
console.log(`[Stylus Build ] stdout: ${data}`);
|
||||
});
|
||||
|
||||
stylusCMD.stderr.on('data', (data) => {
|
||||
console.error(`[Stylus Build ] stderr: ${data}`);
|
||||
});
|
||||
stylusCMD.stderr.on('data', data => {
|
||||
console.error(`[Stylus Build ] stderr: ${data}`);
|
||||
});
|
||||
|
||||
stylusCMD.on('close', (code) => {
|
||||
const message = `[Stylus Build ] child process exited with code ${code}`
|
||||
stylusCMD.on('close', code => {
|
||||
const message = `[Stylus Build ] child process exited with code ${code}`;
|
||||
|
||||
if (code !== 0) {
|
||||
console.error(message);
|
||||
process.exit(code)
|
||||
}
|
||||
console.log(message);
|
||||
});
|
||||
} else {
|
||||
return
|
||||
if (code !== 0) {
|
||||
console.error(message);
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
console.log(message);
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1,15 +1,18 @@
|
||||
import cssnano from 'cssnano';
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
const files = fs.readdirSync(path.resolve('lib/themes'))
|
||||
const files = fs.readdirSync(path.resolve('lib/themes'));
|
||||
|
||||
files.forEach(file => {
|
||||
file = path.resolve('lib/themes', file)
|
||||
cssnano.process(fs.readFileSync(file)).then(result => {
|
||||
fs.writeFileSync(file, result.css)
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
file = path.resolve('lib/themes', file);
|
||||
cssnano
|
||||
.process(fs.readFileSync(file))
|
||||
.then(result => {
|
||||
fs.writeFileSync(file, result.css);
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
@ -1 +1 @@
|
||||
<h1>To infinity and Beyond!</h1>
|
||||
<h1>To infinity and Beyond!</h1>
|
||||
|
17
docs/cdn.md
17
docs/cdn.md
@ -6,7 +6,7 @@ Recommended: [jsDelivr](//cdn.jsdelivr.net), which will reflect the latest versi
|
||||
|
||||
```html
|
||||
<!-- load css -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/themes/vue.css">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/themes/vue.css" />
|
||||
|
||||
<!-- load script -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.js"></script>
|
||||
@ -18,7 +18,10 @@ Alternatively, use [compressed files](#compressed-file).
|
||||
|
||||
```html
|
||||
<!-- load css -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4.10.2/themes/vue.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/docsify@4.10.2/themes/vue.css"
|
||||
/>
|
||||
|
||||
<!-- load script -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4.10.2/lib/docsify.js"></script>
|
||||
@ -28,7 +31,10 @@ Alternatively, use [compressed files](#compressed-file).
|
||||
|
||||
```html
|
||||
<!-- load css -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css"
|
||||
/>
|
||||
|
||||
<!-- load script -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
@ -36,7 +42,10 @@ Alternatively, use [compressed files](#compressed-file).
|
||||
|
||||
```html
|
||||
<!-- load css -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4.10.2/lib/themes/vue.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/docsify@4.10.2/lib/themes/vue.css"
|
||||
/>
|
||||
|
||||
<!-- load script -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4.10.2/lib/docsify.min.js"></script>
|
||||
|
@ -11,8 +11,8 @@ Set `coverpage` to **true**, and create a `_coverpage.md`:
|
||||
|
||||
<script>
|
||||
window.$docsify = {
|
||||
coverpage: true
|
||||
}
|
||||
coverpage: true,
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
```
|
||||
@ -81,7 +81,7 @@ Now, you can set
|
||||
|
||||
```js
|
||||
window.$docsify = {
|
||||
coverpage: ['/', '/zh-cn/']
|
||||
coverpage: ['/', '/zh-cn/'],
|
||||
};
|
||||
```
|
||||
|
||||
@ -91,7 +91,7 @@ Or a special file name
|
||||
window.$docsify = {
|
||||
coverpage: {
|
||||
'/': 'cover.md',
|
||||
'/zh-cn/': 'cover.md'
|
||||
}
|
||||
'/zh-cn/': 'cover.md',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
@ -27,8 +27,8 @@ Alternatively, you can create a custom markdown-based navigation file by setting
|
||||
|
||||
<script>
|
||||
window.$docsify = {
|
||||
loadNavbar: true
|
||||
}
|
||||
loadNavbar: true,
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
```
|
||||
@ -36,8 +36,8 @@ Alternatively, you can create a custom markdown-based navigation file by setting
|
||||
```markdown
|
||||
<!-- _navbar.md -->
|
||||
|
||||
* [En](/)
|
||||
* [chinese](/zh-cn/)
|
||||
- [En](/)
|
||||
- [chinese](/zh-cn/)
|
||||
```
|
||||
|
||||
!> You need to create a `.nojekyll` in `./docs` to prevent GitHub Pages from ignoring files that begin with an underscore.
|
||||
@ -51,19 +51,19 @@ You can create sub-lists by indenting items that are under a certain parent.
|
||||
```markdown
|
||||
<!-- _navbar.md -->
|
||||
|
||||
* Getting started
|
||||
- Getting started
|
||||
|
||||
* [Quick start](quickstart.md)
|
||||
* [Writing more pages](more-pages.md)
|
||||
* [Custom navbar](custom-navbar.md)
|
||||
* [Cover page](cover.md)
|
||||
- [Quick start](quickstart.md)
|
||||
- [Writing more pages](more-pages.md)
|
||||
- [Custom navbar](custom-navbar.md)
|
||||
- [Cover page](cover.md)
|
||||
|
||||
* Configuration
|
||||
* [Configuration](configuration.md)
|
||||
* [Themes](themes.md)
|
||||
* [Using plugins](plugins.md)
|
||||
* [Markdown configuration](markdown.md)
|
||||
* [Language highlight](language-highlight.md)
|
||||
- Configuration
|
||||
- [Configuration](configuration.md)
|
||||
- [Themes](themes.md)
|
||||
- [Using plugins](plugins.md)
|
||||
- [Markdown configuration](markdown.md)
|
||||
- [Language highlight](language-highlight.md)
|
||||
```
|
||||
|
||||
renders as
|
||||
@ -80,7 +80,7 @@ If you use the [emoji plugin](plugins#emoji):
|
||||
<script>
|
||||
window.$docsify = {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/emoji.min.js"></script>
|
||||
@ -91,6 +91,6 @@ you could, for example, use flag emojis in your custom navbar Markdown file:
|
||||
```markdown
|
||||
<!-- _navbar.md -->
|
||||
|
||||
* [:us:, :uk:](/)
|
||||
* [:cn:](/zh-cn/)
|
||||
- [:us:, :uk:](/)
|
||||
- [:cn:](/zh-cn/)
|
||||
```
|
||||
|
@ -96,14 +96,14 @@ When using the HTML5 router, you need to set up redirect rules that redirect all
|
||||
|
||||
## AWS Amplify
|
||||
|
||||
1. Set the routerMode in the Docsify project `index.html` to *history* mode.
|
||||
1. Set the routerMode in the Docsify project `index.html` to _history_ mode.
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.$docsify = {
|
||||
loadSidebar: true,
|
||||
routerMode: 'history'
|
||||
}
|
||||
window.$docsify = {
|
||||
loadSidebar: true,
|
||||
routerMode: 'history',
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
@ -125,17 +125,15 @@ frontend:
|
||||
- '**/*'
|
||||
cache:
|
||||
paths: []
|
||||
|
||||
```
|
||||
|
||||
6. Add the following Redirect rules in their displayed order. Note that the second record is a PNG image where you can change it with any image format you are using.
|
||||
|
||||
| Source address | Target address | Type |
|
||||
|----------------|----------------|---------------|
|
||||
| /<*>.md | /<*>.md | 200 (Rewrite) |
|
||||
| /<*>.png | /<*>.png | 200 (Rewrite) |
|
||||
| /<*> | /index.html | 200 (Rewrite) |
|
||||
|
||||
| -------------- | -------------- | ------------- |
|
||||
| /<\*>.md | /<\*>.md | 200 (Rewrite) |
|
||||
| /<\*>.png | /<\*>.png | 200 (Rewrite) |
|
||||
| /<\*> | /index.html | 200 (Rewrite) |
|
||||
|
||||
## Docker
|
||||
|
||||
@ -144,10 +142,10 @@ frontend:
|
||||
You need prepare the initial files instead of making them inside the container.
|
||||
See the [Quickstart](https://docsify.js.org/#/quickstart) section for instructions on how to create these files manually or using [docsify-cli](https://github.com/docsifyjs/docsify-cli).
|
||||
|
||||
```sh
|
||||
index.html
|
||||
README.md
|
||||
```
|
||||
```sh
|
||||
index.html
|
||||
README.md
|
||||
```
|
||||
|
||||
- Create Dockerfile
|
||||
|
||||
@ -180,4 +178,3 @@ frontend:
|
||||
```sh
|
||||
docker run -itp 3000:3000 --name=docsify -v $(pwd):/docs docsify/demo
|
||||
```
|
||||
|
||||
|
@ -26,11 +26,11 @@ Currently, file extensions are automatically recognized and embedded in differen
|
||||
|
||||
These types are supported:
|
||||
|
||||
* **iframe** `.html`, `.htm`
|
||||
* **markdown** `.markdown`, `.md`
|
||||
* **audio** `.mp3`
|
||||
* **video** `.mp4`, `.ogg`
|
||||
* **code** other file extension
|
||||
- **iframe** `.html`, `.htm`
|
||||
- **markdown** `.markdown`, `.md`
|
||||
- **audio** `.mp3`
|
||||
- **video** `.mp4`, `.ogg`
|
||||
- **code** other file extension
|
||||
|
||||
Of course, you can force the specified type. For example, a Markdown file can be embedded as a code block by setting `:type=code`.
|
||||
|
||||
@ -74,6 +74,7 @@ Example:
|
||||
If you embed the file as `iframe`, `audio` and `video`, then you may need to set the attributes of these tags.
|
||||
|
||||
?> Note, for the `audio` and `video` types, docsify adds the `controls` attribute by default. When you want add more attributes, the `controls` attribute need to be added manually if need be.
|
||||
|
||||
```md
|
||||
[filename](_media/example.mp4 ':include :type=video controls width=100%')
|
||||
```
|
||||
@ -114,11 +115,11 @@ Start by viewing a gist on `gist.github.com`. For the purposes of this guide, we
|
||||
|
||||
Identify the following items from the gist:
|
||||
|
||||
Field | Example | Description
|
||||
--- | --- | ---
|
||||
**Username** | `anikethsaha` | The gist's owner.
|
||||
**Gist ID** | `c2bece08f27c4277001f123898d16a7c` | Identifier for the gist. This is fixed for the gist's lifetime.
|
||||
**Filename** | `content.md` | Select a name of a file in the gist. This needed even on a single-file gist for embedding to work.
|
||||
| Field | Example | Description |
|
||||
| ------------ | ---------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| **Username** | `anikethsaha` | The gist's owner. |
|
||||
| **Gist ID** | `c2bece08f27c4277001f123898d16a7c` | Identifier for the gist. This is fixed for the gist's lifetime. |
|
||||
| **Filename** | `content.md` | Select a name of a file in the gist. This needed even on a single-file gist for embedding to work. |
|
||||
|
||||
You will need those to build the _raw gist URL_ for the target file. This has the following format:
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
Docsify uses [Prism](https://prismjs.com) to highlight code blocks in your pages. Prism supports the following languages by default:
|
||||
|
||||
* Markup - `markup`, `html`, `xml`, `svg`, `mathml`, `ssml`, `atom`, `rss`
|
||||
* CSS - `css`
|
||||
* C-like - `clike`
|
||||
* JavaScript - `javascript`, `js`
|
||||
- Markup - `markup`, `html`, `xml`, `svg`, `mathml`, `ssml`, `atom`, `rss`
|
||||
- CSS - `css`
|
||||
- C-like - `clike`
|
||||
- JavaScript - `javascript`, `js`
|
||||
|
||||
Support for [additional languages](https://prismjs.com/#supported-languages) is available by loading the language-specific [grammar files](https://cdn.jsdelivr.net/npm/prismjs@1/components/) via CDN:
|
||||
|
||||
@ -29,7 +29,7 @@ echo "hello"
|
||||
```
|
||||
|
||||
```php
|
||||
function getAdder(int $x): int
|
||||
function getAdder(int $x): int
|
||||
{
|
||||
return 123;
|
||||
}
|
||||
@ -48,18 +48,19 @@ echo "hello"
|
||||
```
|
||||
|
||||
```php
|
||||
function getAdder(int $x): int
|
||||
function getAdder(int $x): int
|
||||
{
|
||||
return 123;
|
||||
}
|
||||
```
|
||||
|
||||
## Highlighting Dynamic Content
|
||||
|
||||
Code blocks [dynamically created from javascript](https://docsify.js.org/#/configuration?id=executescript) can be highlighted using the method `Prism.highlightElement` like so:
|
||||
|
||||
```javascript
|
||||
const code = document.createElement("code");
|
||||
const code = document.createElement('code');
|
||||
code.innerHTML = "console.log('Hello World!')";
|
||||
code.setAttribute("class", "lang-javascript");
|
||||
code.setAttribute('class', 'lang-javascript');
|
||||
Prism.highlightElement(code);
|
||||
```
|
||||
|
@ -9,10 +9,10 @@ window.$docsify = {
|
||||
renderer: {
|
||||
link() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
?> Configuration Options Reference: [marked documentation](https://marked.js.org/#/USING_ADVANCED.md)
|
||||
@ -24,9 +24,9 @@ window.$docsify = {
|
||||
markdown(marked, renderer) {
|
||||
// ...
|
||||
|
||||
return marked
|
||||
}
|
||||
}
|
||||
return marked;
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Supports mermaid
|
||||
@ -43,14 +43,17 @@ window.$docsify = {
|
||||
markdown: {
|
||||
renderer: {
|
||||
code(code, lang) {
|
||||
if (lang === "mermaid") {
|
||||
if (lang === 'mermaid') {
|
||||
return /* html */ `
|
||||
<div class="mermaid">${mermaid.render('mermaid-svg-' + num++, code)}</div>
|
||||
<div class="mermaid">${mermaid.render(
|
||||
'mermaid-svg-' + num++,
|
||||
code
|
||||
)}</div>
|
||||
`;
|
||||
}
|
||||
return this.origin.code.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
@ -34,8 +34,8 @@ First, you need to set `loadSidebar` to **true**. Details are available in the [
|
||||
|
||||
<script>
|
||||
window.$docsify = {
|
||||
loadSidebar: true
|
||||
}
|
||||
loadSidebar: true,
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
```
|
||||
@ -45,8 +45,8 @@ Create the `_sidebar.md`:
|
||||
```markdown
|
||||
<!-- docs/_sidebar.md -->
|
||||
|
||||
* [Home](/)
|
||||
* [Guide](guide.md)
|
||||
- [Home](/)
|
||||
- [Guide](guide.md)
|
||||
```
|
||||
|
||||
You need to create a `.nojekyll` in `./docs` to prevent GitHub Pages from ignoring files that begin with an underscore.
|
||||
@ -76,9 +76,9 @@ You can specify `alias` to avoid unnecessary fallback.
|
||||
window.$docsify = {
|
||||
loadSidebar: true,
|
||||
alias: {
|
||||
'/.*/_sidebar.md': '/_sidebar.md'
|
||||
}
|
||||
}
|
||||
'/.*/_sidebar.md': '/_sidebar.md',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
@ -90,8 +90,9 @@ A page's `title` tag is generated from the _selected_ sidebar item name. For bet
|
||||
|
||||
```markdown
|
||||
<!-- docs/_sidebar.md -->
|
||||
* [Home](/)
|
||||
* [Guide](guide.md "The greatest guide in the world")
|
||||
|
||||
- [Home](/)
|
||||
- [Guide](guide.md 'The greatest guide in the world')
|
||||
```
|
||||
|
||||
## Table of Contents
|
||||
@ -106,8 +107,8 @@ A custom sidebar can also automatically generate a table of contents by setting
|
||||
<script>
|
||||
window.$docsify = {
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 2
|
||||
}
|
||||
subMaxLevel: 2,
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
```
|
||||
|
57
docs/pwa.md
57
docs/pwa.md
@ -8,7 +8,7 @@ It is also very easy to use.
|
||||
|
||||
Create a `sw.js` file in your project's root directory and copy the following code:
|
||||
|
||||
*sw.js*
|
||||
_sw.js_
|
||||
|
||||
```js
|
||||
/* ===========================================================
|
||||
@ -19,24 +19,24 @@ Create a `sw.js` file in your project's root directory and copy the following co
|
||||
* Register service worker.
|
||||
* ========================================================== */
|
||||
|
||||
const RUNTIME = 'docsify'
|
||||
const RUNTIME = 'docsify';
|
||||
const HOSTNAME_WHITELIST = [
|
||||
self.location.hostname,
|
||||
'fonts.gstatic.com',
|
||||
'fonts.googleapis.com',
|
||||
'cdn.jsdelivr.net'
|
||||
]
|
||||
'cdn.jsdelivr.net',
|
||||
];
|
||||
|
||||
// The Util Function to hack URLs of intercepted requests
|
||||
const getFixedUrl = (req) => {
|
||||
const now = Date.now()
|
||||
const url = new URL(req.url)
|
||||
const getFixedUrl = req => {
|
||||
const now = Date.now();
|
||||
const url = new URL(req.url);
|
||||
|
||||
// 1. fixed http URL
|
||||
// Just keep syncing with location.protocol
|
||||
// fetch(httpURL) belongs to active mixed content.
|
||||
// And fetch(httpRequest) is not supported yet.
|
||||
url.protocol = self.location.protocol
|
||||
url.protocol = self.location.protocol;
|
||||
|
||||
// 2. add query for caching-busting.
|
||||
// Github Pages served with Cache-Control: max-age=600
|
||||
@ -44,10 +44,10 @@ const getFixedUrl = (req) => {
|
||||
// Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
|
||||
// Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
|
||||
if (url.hostname === self.location.hostname) {
|
||||
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
|
||||
url.search += (url.search ? '&' : '?') + 'cache-bust=' + now;
|
||||
}
|
||||
return url.href
|
||||
}
|
||||
return url.href;
|
||||
};
|
||||
|
||||
/**
|
||||
* @Lifecycle Activate
|
||||
@ -56,8 +56,8 @@ const getFixedUrl = (req) => {
|
||||
* waitUntil(): activating ====> activated
|
||||
*/
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
|
||||
/**
|
||||
* @Functional Fetch
|
||||
@ -71,10 +71,10 @@ self.addEventListener('fetch', event => {
|
||||
// Stale-while-revalidate
|
||||
// similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
|
||||
// Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
|
||||
const cached = caches.match(event.request)
|
||||
const fixedUrl = getFixedUrl(event.request)
|
||||
const fetched = fetch(fixedUrl, { cache: 'no-store' })
|
||||
const fetchedCopy = fetched.then(resp => resp.clone())
|
||||
const cached = caches.match(event.request);
|
||||
const fixedUrl = getFixedUrl(event.request);
|
||||
const fetched = fetch(fixedUrl, { cache: 'no-store' });
|
||||
const fetchedCopy = fetched.then(resp => resp.clone());
|
||||
|
||||
// Call respondWith() with whatever we get first.
|
||||
// If the fetch fails (e.g disconnected), wait for the cache.
|
||||
@ -83,29 +83,36 @@ self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
Promise.race([fetched.catch(_ => cached), cached])
|
||||
.then(resp => resp || fetched)
|
||||
.catch(_ => { /* eat any errors */ })
|
||||
)
|
||||
.catch(_ => {
|
||||
/* eat any errors */
|
||||
})
|
||||
);
|
||||
|
||||
// Update the cache with the version we fetched (only for ok status)
|
||||
event.waitUntil(
|
||||
Promise.all([fetchedCopy, caches.open(RUNTIME)])
|
||||
.then(([response, cache]) => response.ok && cache.put(event.request, response))
|
||||
.catch(_ => { /* eat any errors */ })
|
||||
)
|
||||
.then(
|
||||
([response, cache]) =>
|
||||
response.ok && cache.put(event.request, response)
|
||||
)
|
||||
.catch(_ => {
|
||||
/* eat any errors */
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
## Register
|
||||
|
||||
Now, register it in your `index.html`. It only works on some modern browsers, so we need to check:
|
||||
|
||||
*index.html*
|
||||
_index.html_
|
||||
|
||||
```html
|
||||
<script>
|
||||
if (typeof navigator.serviceWorker !== 'undefined') {
|
||||
navigator.serviceWorker.register('sw.js')
|
||||
navigator.serviceWorker.register('sw.js');
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
@ -8,7 +8,12 @@
|
||||
<link rel="stylesheet" href="/themes/dark.css" title="dark" disabled />
|
||||
<link rel="stylesheet" href="/themes/buble.css" title="buble" disabled />
|
||||
<link rel="stylesheet" href="/themes/pure.css" title="pure" disabled />
|
||||
<link rel="stylesheet" href="/themes/dolphin.css" title="dolphin" disabled />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="/themes/dolphin.css"
|
||||
title="dolphin"
|
||||
disabled
|
||||
/>
|
||||
<style>
|
||||
nav.app-nav li ul {
|
||||
min-width: 100px;
|
||||
|
@ -41,8 +41,9 @@
|
||||
"docker:test:integration": "npm run docker:cli -- test:integration",
|
||||
"docker:test:unit": "npm run docker:cli -- test:unit",
|
||||
"docker:test": "npm run docker:cli -- test",
|
||||
"prettier": "prettier . --write",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "eslint .",
|
||||
"lint": "prettier . --check && eslint .",
|
||||
"postinstall": "opencollective-postinstall",
|
||||
"prepare": "npm run build",
|
||||
"pub:next": "cross-env RELEASE_TAG=next sh build/release.sh",
|
||||
|
@ -3,7 +3,6 @@ import { Render } from './render/index.js';
|
||||
import { Fetch } from './fetch/index.js';
|
||||
import { Events } from './event/index.js';
|
||||
import { VirtualRoutes } from './virtual-routes/index.js';
|
||||
import initGlobalAPI from './global-api.js';
|
||||
|
||||
import config from './config.js';
|
||||
import { isFn } from './util/core.js';
|
||||
@ -16,11 +15,11 @@ export class Docsify extends Fetch(
|
||||
// eslint-disable-next-line new-cap
|
||||
Events(Render(VirtualRoutes(Router(Lifecycle(Object)))))
|
||||
) {
|
||||
config = config(this);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.config = config(this);
|
||||
|
||||
this.initLifecycle(); // Init hooks
|
||||
this.initPlugin(); // Install plugins
|
||||
this.callHook('init');
|
||||
@ -46,8 +45,3 @@ export class Docsify extends Fetch(
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global API
|
||||
*/
|
||||
initGlobalAPI();
|
||||
|
@ -3,7 +3,7 @@ import { hyphenate, isPrimitive } from './util/core.js';
|
||||
|
||||
const currentScript = document.currentScript;
|
||||
|
||||
/** @param {import('./Docsify').Docsify} vm */
|
||||
/** @param {import('./Docsify.js').Docsify} vm */
|
||||
export default function (vm) {
|
||||
const config = Object.assign(
|
||||
{
|
||||
|
@ -1,9 +1,11 @@
|
||||
import Tweezer from 'tweezer.js';
|
||||
import { isMobile } from '../util/env.js';
|
||||
import { body, on } from '../util/dom.js';
|
||||
import * as sidebar from './sidebar.js';
|
||||
import { scrollIntoView, scroll2Top } from './scroll.js';
|
||||
import * as dom from '../util/dom.js';
|
||||
import { removeParams } from '../router/util.js';
|
||||
import config from '../config.js';
|
||||
|
||||
/** @typedef {import('../Docsify').Constructor} Constructor */
|
||||
/** @typedef {import('../Docsify.js').Constructor} Constructor */
|
||||
|
||||
/**
|
||||
* @template {!Constructor} T
|
||||
@ -18,29 +20,300 @@ export function Events(Base) {
|
||||
if (source !== 'history') {
|
||||
// Scroll to ID if specified
|
||||
if (this.route.query.id) {
|
||||
scrollIntoView(this.route.path, this.route.query.id);
|
||||
this.#scrollIntoView(this.route.path, this.route.query.id);
|
||||
}
|
||||
// Scroll to top if a link was clicked and auto2top is enabled
|
||||
if (source === 'navigate') {
|
||||
auto2top && scroll2Top(auto2top);
|
||||
auto2top && this.#scroll2Top(auto2top);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.loadNavbar) {
|
||||
sidebar.getAndActive(this.router, 'nav');
|
||||
this.__getAndActive(this.router, 'nav');
|
||||
}
|
||||
}
|
||||
|
||||
initEvent() {
|
||||
// Bind toggle button
|
||||
sidebar.btn('button.sidebar-toggle', this.router);
|
||||
sidebar.collapse('.sidebar', this.router);
|
||||
this.#btn('button.sidebar-toggle', this.router);
|
||||
this.#collapse('.sidebar', this.router);
|
||||
// Bind sticky effect
|
||||
if (this.config.coverpage) {
|
||||
!isMobile && on('scroll', sidebar.sticky);
|
||||
!isMobile && on('scroll', this.__sticky);
|
||||
} else {
|
||||
body.classList.add('sticky');
|
||||
}
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
#nav = {};
|
||||
|
||||
#hoverOver = false;
|
||||
#scroller = null;
|
||||
#enableScrollEvent = true;
|
||||
#coverHeight = 0;
|
||||
|
||||
#scrollTo(el, offset = 0) {
|
||||
if (this.#scroller) {
|
||||
this.#scroller.stop();
|
||||
}
|
||||
|
||||
this.#enableScrollEvent = false;
|
||||
this.#scroller = new Tweezer({
|
||||
start: window.pageYOffset,
|
||||
end:
|
||||
Math.round(el.getBoundingClientRect().top) +
|
||||
window.pageYOffset -
|
||||
offset,
|
||||
duration: 500,
|
||||
})
|
||||
.on('tick', v => window.scrollTo(0, v))
|
||||
.on('done', () => {
|
||||
this.#enableScrollEvent = true;
|
||||
this.#scroller = null;
|
||||
})
|
||||
.begin();
|
||||
}
|
||||
|
||||
#highlight(path) {
|
||||
if (!this.#enableScrollEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sidebar = dom.getNode('.sidebar');
|
||||
const anchors = dom.findAll('.anchor');
|
||||
const wrap = dom.find(sidebar, '.sidebar-nav');
|
||||
let active = dom.find(sidebar, 'li.active');
|
||||
const doc = document.documentElement;
|
||||
const top =
|
||||
((doc && doc.scrollTop) || document.body.scrollTop) - this.#coverHeight;
|
||||
let last;
|
||||
|
||||
for (const node of anchors) {
|
||||
if (node.offsetTop > top) {
|
||||
if (!last) {
|
||||
last = node;
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
last = node;
|
||||
}
|
||||
}
|
||||
|
||||
if (!last) {
|
||||
return;
|
||||
}
|
||||
|
||||
const li = this.#nav[this.#getNavKey(path, last.getAttribute('data-id'))];
|
||||
|
||||
if (!li || li === active) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 (!this.#hoverOver && dom.body.classList.contains('sticky')) {
|
||||
const height = sidebar.clientHeight;
|
||||
const curOffset = 0;
|
||||
const cur = active.offsetTop + active.clientHeight + 40;
|
||||
const isInView =
|
||||
active.offsetTop >= wrap.scrollTop && cur <= wrap.scrollTop + height;
|
||||
const notThan = cur - curOffset < height;
|
||||
|
||||
sidebar.scrollTop = isInView
|
||||
? wrap.scrollTop
|
||||
: notThan
|
||||
? curOffset
|
||||
: cur - height;
|
||||
}
|
||||
}
|
||||
|
||||
#getNavKey(path, id) {
|
||||
return `${decodeURIComponent(path)}?id=${decodeURIComponent(id)}`;
|
||||
}
|
||||
|
||||
__scrollActiveSidebar(router) {
|
||||
const cover = dom.find('.cover.show');
|
||||
this.#coverHeight = cover ? cover.offsetHeight : 0;
|
||||
|
||||
const sidebar = dom.getNode('.sidebar');
|
||||
let lis = [];
|
||||
if (sidebar !== null && sidebar !== undefined) {
|
||||
lis = dom.findAll(sidebar, 'li');
|
||||
}
|
||||
|
||||
for (const li of lis) {
|
||||
const a = li.querySelector('a');
|
||||
if (!a) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let href = a.getAttribute('href');
|
||||
|
||||
if (href !== '/') {
|
||||
const {
|
||||
query: { id },
|
||||
path,
|
||||
} = router.parse(href);
|
||||
if (id) {
|
||||
href = this.#getNavKey(path, id);
|
||||
}
|
||||
}
|
||||
|
||||
if (href) {
|
||||
this.#nav[decodeURIComponent(href)] = li;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const path = removeParams(router.getCurrentPath());
|
||||
dom.off('scroll', () => this.#highlight(path));
|
||||
dom.on('scroll', () => this.#highlight(path));
|
||||
dom.on(sidebar, 'mouseover', () => {
|
||||
this.#hoverOver = true;
|
||||
});
|
||||
dom.on(sidebar, 'mouseleave', () => {
|
||||
this.#hoverOver = false;
|
||||
});
|
||||
}
|
||||
|
||||
#scrollIntoView(path, id) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const topMargin = config().topMargin;
|
||||
// Use [id='1234'] instead of #id to handle special cases such as reserved characters and pure number id
|
||||
// https://stackoverflow.com/questions/37270787/uncaught-syntaxerror-failed-to-execute-queryselector-on-document
|
||||
const section = dom.find("[id='" + id + "']");
|
||||
section && this.#scrollTo(section, topMargin);
|
||||
|
||||
const li = this.#nav[this.#getNavKey(path, id)];
|
||||
const sidebar = dom.getNode('.sidebar');
|
||||
const active = dom.find(sidebar, 'li.active');
|
||||
active && active.classList.remove('active');
|
||||
li && li.classList.add('active');
|
||||
}
|
||||
|
||||
#scrollEl = dom.$.scrollingElement || dom.$.documentElement;
|
||||
|
||||
#scroll2Top(offset = 0) {
|
||||
this.#scrollEl.scrollTop = offset === true ? 0 : Number(offset);
|
||||
}
|
||||
|
||||
/** @readonly */
|
||||
#title = dom.$.title;
|
||||
|
||||
/**
|
||||
* Toggle button
|
||||
* @param {Element} el Button to be toggled
|
||||
* @void
|
||||
*/
|
||||
#btn(el) {
|
||||
const toggle = _ => dom.body.classList.toggle('close');
|
||||
|
||||
el = dom.getNode(el);
|
||||
if (el === null || el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.on(el, 'click', e => {
|
||||
e.stopPropagation();
|
||||
toggle();
|
||||
});
|
||||
|
||||
isMobile &&
|
||||
dom.on(
|
||||
dom.body,
|
||||
'click',
|
||||
_ => dom.body.classList.contains('close') && toggle()
|
||||
);
|
||||
}
|
||||
|
||||
#collapse(el) {
|
||||
el = dom.getNode(el);
|
||||
if (el === null || el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.on(el, 'click', ({ target }) => {
|
||||
if (
|
||||
target.nodeName === 'A' &&
|
||||
target.nextSibling &&
|
||||
target.nextSibling.classList &&
|
||||
target.nextSibling.classList.contains('app-sub-sidebar')
|
||||
) {
|
||||
dom.toggleClass(target.parentNode, 'collapse');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
__sticky = () => {
|
||||
const cover = dom.getNode('section.cover');
|
||||
if (!cover) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coverHeight = cover.getBoundingClientRect().height;
|
||||
|
||||
if (
|
||||
window.pageYOffset >= coverHeight ||
|
||||
cover.classList.contains('hidden')
|
||||
) {
|
||||
dom.toggleClass(dom.body, 'add', 'sticky');
|
||||
} else {
|
||||
dom.toggleClass(dom.body, 'remove', 'sticky');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get and active link
|
||||
* @param {Object} router Router
|
||||
* @param {String|Element} el Target element
|
||||
* @param {Boolean} isParent Active parent
|
||||
* @param {Boolean} autoTitle Automatically set title
|
||||
* @return {Element} Active element
|
||||
*/
|
||||
__getAndActive(router, el, isParent, autoTitle) {
|
||||
el = dom.getNode(el);
|
||||
let links = [];
|
||||
if (el !== null && el !== undefined) {
|
||||
links = dom.findAll(el, 'a');
|
||||
}
|
||||
|
||||
const hash = decodeURI(router.toURL(router.getCurrentPath()));
|
||||
let target;
|
||||
|
||||
links
|
||||
.sort((a, b) => b.href.length - a.href.length)
|
||||
.forEach(a => {
|
||||
const href = decodeURI(a.getAttribute('href'));
|
||||
const node = isParent ? a.parentNode : a;
|
||||
|
||||
a.title = a.title || a.innerText;
|
||||
|
||||
if (hash.indexOf(href) === 0 && !target) {
|
||||
target = a;
|
||||
dom.toggleClass(node, 'add', 'active');
|
||||
} else {
|
||||
dom.toggleClass(node, 'remove', 'active');
|
||||
}
|
||||
});
|
||||
|
||||
if (autoTitle) {
|
||||
dom.$.title = target
|
||||
? target.title || `${target.innerText} - ${this.#title}`
|
||||
: this.#title;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,163 +0,0 @@
|
||||
import Tweezer from 'tweezer.js';
|
||||
import { isMobile } from '../util/env.js';
|
||||
import * as dom from '../util/dom.js';
|
||||
import { removeParams } from '../router/util.js';
|
||||
import config from '../config.js';
|
||||
|
||||
const nav = {};
|
||||
let hoverOver = false;
|
||||
let scroller = null;
|
||||
let enableScrollEvent = true;
|
||||
let coverHeight = 0;
|
||||
|
||||
function scrollTo(el, offset = 0) {
|
||||
if (scroller) {
|
||||
scroller.stop();
|
||||
}
|
||||
|
||||
enableScrollEvent = false;
|
||||
scroller = new Tweezer({
|
||||
start: window.pageYOffset,
|
||||
end:
|
||||
Math.round(el.getBoundingClientRect().top) + window.pageYOffset - offset,
|
||||
duration: 500,
|
||||
})
|
||||
.on('tick', v => window.scrollTo(0, v))
|
||||
.on('done', () => {
|
||||
enableScrollEvent = true;
|
||||
scroller = null;
|
||||
})
|
||||
.begin();
|
||||
}
|
||||
|
||||
function highlight(path) {
|
||||
if (!enableScrollEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sidebar = dom.getNode('.sidebar');
|
||||
const anchors = dom.findAll('.anchor');
|
||||
const wrap = dom.find(sidebar, '.sidebar-nav');
|
||||
let active = dom.find(sidebar, 'li.active');
|
||||
const doc = document.documentElement;
|
||||
const top = ((doc && doc.scrollTop) || document.body.scrollTop) - coverHeight;
|
||||
let last;
|
||||
|
||||
for (const node of anchors) {
|
||||
if (node.offsetTop > top) {
|
||||
if (!last) {
|
||||
last = node;
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
last = node;
|
||||
}
|
||||
}
|
||||
|
||||
if (!last) {
|
||||
return;
|
||||
}
|
||||
|
||||
const li = nav[getNavKey(path, last.getAttribute('data-id'))];
|
||||
|
||||
if (!li || li === active) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 (!hoverOver && dom.body.classList.contains('sticky')) {
|
||||
const height = sidebar.clientHeight;
|
||||
const curOffset = 0;
|
||||
const cur = active.offsetTop + active.clientHeight + 40;
|
||||
const isInView =
|
||||
active.offsetTop >= wrap.scrollTop && cur <= wrap.scrollTop + height;
|
||||
const notThan = cur - curOffset < height;
|
||||
|
||||
sidebar.scrollTop = isInView
|
||||
? wrap.scrollTop
|
||||
: notThan
|
||||
? curOffset
|
||||
: cur - height;
|
||||
}
|
||||
}
|
||||
|
||||
function getNavKey(path, id) {
|
||||
return `${decodeURIComponent(path)}?id=${decodeURIComponent(id)}`;
|
||||
}
|
||||
|
||||
export function scrollActiveSidebar(router) {
|
||||
const cover = dom.find('.cover.show');
|
||||
coverHeight = cover ? cover.offsetHeight : 0;
|
||||
|
||||
const sidebar = dom.getNode('.sidebar');
|
||||
let lis = [];
|
||||
if (sidebar !== null && sidebar !== undefined) {
|
||||
lis = dom.findAll(sidebar, 'li');
|
||||
}
|
||||
|
||||
for (const li of lis) {
|
||||
const a = li.querySelector('a');
|
||||
if (!a) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let href = a.getAttribute('href');
|
||||
|
||||
if (href !== '/') {
|
||||
const {
|
||||
query: { id },
|
||||
path,
|
||||
} = router.parse(href);
|
||||
if (id) {
|
||||
href = getNavKey(path, id);
|
||||
}
|
||||
}
|
||||
|
||||
if (href) {
|
||||
nav[decodeURIComponent(href)] = li;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMobile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const path = removeParams(router.getCurrentPath());
|
||||
dom.off('scroll', () => highlight(path));
|
||||
dom.on('scroll', () => highlight(path));
|
||||
dom.on(sidebar, 'mouseover', () => {
|
||||
hoverOver = true;
|
||||
});
|
||||
dom.on(sidebar, 'mouseleave', () => {
|
||||
hoverOver = false;
|
||||
});
|
||||
}
|
||||
|
||||
export function scrollIntoView(path, id) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const topMargin = config().topMargin;
|
||||
// Use [id='1234'] instead of #id to handle special cases such as reserved characters and pure number id
|
||||
// https://stackoverflow.com/questions/37270787/uncaught-syntaxerror-failed-to-execute-queryselector-on-document
|
||||
const section = dom.find("[id='" + id + "']");
|
||||
section && scrollTo(section, topMargin);
|
||||
|
||||
const li = nav[getNavKey(path, id)];
|
||||
const sidebar = dom.getNode('.sidebar');
|
||||
const active = dom.find(sidebar, 'li.active');
|
||||
active && active.classList.remove('active');
|
||||
li && li.classList.add('active');
|
||||
}
|
||||
|
||||
const scrollEl = dom.$.scrollingElement || dom.$.documentElement;
|
||||
|
||||
export function scroll2Top(offset = 0) {
|
||||
scrollEl.scrollTop = offset === true ? 0 : Number(offset);
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { isMobile } from '../util/env.js';
|
||||
import * as dom from '../util/dom.js';
|
||||
|
||||
const title = dom.$.title;
|
||||
/**
|
||||
* Toggle button
|
||||
* @param {Element} el Button to be toggled
|
||||
* @void
|
||||
*/
|
||||
export function btn(el) {
|
||||
const toggle = _ => dom.body.classList.toggle('close');
|
||||
|
||||
el = dom.getNode(el);
|
||||
if (el === null || el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.on(el, 'click', e => {
|
||||
e.stopPropagation();
|
||||
toggle();
|
||||
});
|
||||
|
||||
isMobile &&
|
||||
dom.on(
|
||||
dom.body,
|
||||
'click',
|
||||
_ => dom.body.classList.contains('close') && toggle()
|
||||
);
|
||||
}
|
||||
|
||||
export function collapse(el) {
|
||||
el = dom.getNode(el);
|
||||
if (el === null || el === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.on(el, 'click', ({ target }) => {
|
||||
if (
|
||||
target.nodeName === 'A' &&
|
||||
target.nextSibling &&
|
||||
target.nextSibling.classList &&
|
||||
target.nextSibling.classList.contains('app-sub-sidebar')
|
||||
) {
|
||||
dom.toggleClass(target.parentNode, 'collapse');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function sticky() {
|
||||
const cover = dom.getNode('section.cover');
|
||||
if (!cover) {
|
||||
return;
|
||||
}
|
||||
|
||||
const coverHeight = cover.getBoundingClientRect().height;
|
||||
|
||||
if (window.pageYOffset >= coverHeight || cover.classList.contains('hidden')) {
|
||||
dom.toggleClass(dom.body, 'add', 'sticky');
|
||||
} else {
|
||||
dom.toggleClass(dom.body, 'remove', 'sticky');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and active link
|
||||
* @param {Object} router Router
|
||||
* @param {String|Element} el Target element
|
||||
* @param {Boolean} isParent Active parent
|
||||
* @param {Boolean} autoTitle Automatically set title
|
||||
* @return {Element} Active element
|
||||
*/
|
||||
export function getAndActive(router, el, isParent, autoTitle) {
|
||||
el = dom.getNode(el);
|
||||
let links = [];
|
||||
if (el !== null && el !== undefined) {
|
||||
links = dom.findAll(el, 'a');
|
||||
}
|
||||
|
||||
const hash = decodeURI(router.toURL(router.getCurrentPath()));
|
||||
let target;
|
||||
|
||||
links
|
||||
.sort((a, b) => b.href.length - a.href.length)
|
||||
.forEach(a => {
|
||||
const href = decodeURI(a.getAttribute('href'));
|
||||
const node = isParent ? a.parentNode : a;
|
||||
|
||||
a.title = a.title || a.innerText;
|
||||
|
||||
if (hash.indexOf(href) === 0 && !target) {
|
||||
target = a;
|
||||
dom.toggleClass(node, 'add', 'active');
|
||||
} else {
|
||||
dom.toggleClass(node, 'remove', 'active');
|
||||
}
|
||||
});
|
||||
|
||||
if (autoTitle) {
|
||||
dom.$.title = target
|
||||
? target.title || `${target.innerText} - ${title}`
|
||||
: title;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
@ -1,70 +1,70 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { getParentPath, stringifyQuery } from '../router/util.js';
|
||||
import { noop, isExternal } from '../util/core.js';
|
||||
import { getAndActive } from '../event/sidebar.js';
|
||||
import { get } from './ajax.js';
|
||||
import { get } from '../util/ajax.js';
|
||||
|
||||
function loadNested(path, qs, file, next, vm, first) {
|
||||
path = first ? path : path.replace(/\/$/, '');
|
||||
path = getParentPath(path);
|
||||
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
get(
|
||||
vm.router.getFile(path + file) + qs,
|
||||
false,
|
||||
vm.config.requestHeaders
|
||||
).then(next, _error => loadNested(path, qs, file, next, vm));
|
||||
}
|
||||
|
||||
/** @typedef {import('../Docsify').Constructor} Constructor */
|
||||
/** @typedef {import('../Docsify.js').Constructor} Constructor */
|
||||
|
||||
/**
|
||||
* @template {!Constructor} T
|
||||
* @param {T} Base - The class to extend
|
||||
*/
|
||||
export function Fetch(Base) {
|
||||
let last;
|
||||
return class Fetch extends Base {
|
||||
#loadNested(path, qs, file, next, vm, first) {
|
||||
path = first ? path : path.replace(/\/$/, '');
|
||||
path = getParentPath(path);
|
||||
|
||||
const abort = () => last && last.abort && last.abort();
|
||||
const request = (url, requestHeaders) => {
|
||||
abort();
|
||||
last = get(url, true, requestHeaders);
|
||||
return last;
|
||||
};
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const get404Path = (path, config) => {
|
||||
const { notFoundPage, ext } = config;
|
||||
const defaultPath = '_404' + (ext || '.md');
|
||||
let key;
|
||||
let path404;
|
||||
|
||||
switch (typeof notFoundPage) {
|
||||
case 'boolean':
|
||||
path404 = defaultPath;
|
||||
break;
|
||||
case 'string':
|
||||
path404 = notFoundPage;
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
key = Object.keys(notFoundPage)
|
||||
.sort((a, b) => b.length - a.length)
|
||||
.filter(k => path.match(new RegExp('^' + k)))[0];
|
||||
|
||||
path404 = (key && notFoundPage[key]) || defaultPath;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
get(
|
||||
vm.router.getFile(path + file) + qs,
|
||||
false,
|
||||
vm.config.requestHeaders
|
||||
).then(next, _error => this.#loadNested(path, qs, file, next, vm));
|
||||
}
|
||||
|
||||
return path404;
|
||||
};
|
||||
#last;
|
||||
|
||||
#abort = () => this.#last && this.#last.abort && this.#last.abort();
|
||||
|
||||
#request = (url, requestHeaders) => {
|
||||
this.#abort();
|
||||
this.#last = get(url, true, requestHeaders);
|
||||
return this.#last;
|
||||
};
|
||||
|
||||
#get404Path = (path, config) => {
|
||||
const { notFoundPage, ext } = config;
|
||||
const defaultPath = '_404' + (ext || '.md');
|
||||
let key;
|
||||
let path404;
|
||||
|
||||
switch (typeof notFoundPage) {
|
||||
case 'boolean':
|
||||
path404 = defaultPath;
|
||||
break;
|
||||
case 'string':
|
||||
path404 = notFoundPage;
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
key = Object.keys(notFoundPage)
|
||||
.sort((a, b) => b.length - a.length)
|
||||
.filter(k => path.match(new RegExp('^' + k)))[0];
|
||||
|
||||
path404 = (key && notFoundPage[key]) || defaultPath;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return path404;
|
||||
};
|
||||
|
||||
return class Fetch extends Base {
|
||||
_loadSideAndNav(path, qs, loadSidebar, cb) {
|
||||
return () => {
|
||||
if (!loadSidebar) {
|
||||
@ -77,7 +77,7 @@ export function Fetch(Base) {
|
||||
};
|
||||
|
||||
// Load sidebar
|
||||
loadNested(path, qs, loadSidebar, fn, this, true);
|
||||
this.#loadNested(path, qs, loadSidebar, fn, this, true);
|
||||
};
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ export function Fetch(Base) {
|
||||
if (typeof contents === 'string') {
|
||||
contentFetched(contents);
|
||||
} else {
|
||||
request(file + qs, requestHeaders).then(
|
||||
this.#request(file + qs, requestHeaders).then(
|
||||
contentFetched,
|
||||
contentFailedToFetch
|
||||
);
|
||||
@ -129,7 +129,7 @@ export function Fetch(Base) {
|
||||
});
|
||||
} else {
|
||||
// if the requested url is not local, just fetch the file
|
||||
request(file + qs, requestHeaders).then(
|
||||
this.#request(file + qs, requestHeaders).then(
|
||||
contentFetched,
|
||||
contentFailedToFetch
|
||||
);
|
||||
@ -137,7 +137,7 @@ export function Fetch(Base) {
|
||||
|
||||
// Load nav
|
||||
loadNavbar &&
|
||||
loadNested(
|
||||
this.#loadNested(
|
||||
path,
|
||||
qs,
|
||||
loadNavbar,
|
||||
@ -216,7 +216,7 @@ export function Fetch(Base) {
|
||||
const newPath = this.router.getFile(
|
||||
path.replace(new RegExp(`^/${local}`), '')
|
||||
);
|
||||
const req = request(newPath + qs, requestHeaders);
|
||||
const req = this.#request(newPath + qs, requestHeaders);
|
||||
|
||||
req.then(
|
||||
(text, opt) =>
|
||||
@ -244,9 +244,9 @@ export function Fetch(Base) {
|
||||
|
||||
const fnLoadSideAndNav = this._loadSideAndNav(path, qs, loadSidebar, cb);
|
||||
if (notFoundPage) {
|
||||
const path404 = get404Path(path, this.config);
|
||||
const path404 = this.#get404Path(path, this.config);
|
||||
|
||||
request(this.router.getFile(path404), requestHeaders).then(
|
||||
this.#request(this.router.getFile(path404), requestHeaders).then(
|
||||
(text, opt) => this._renderMain(text, opt, fnLoadSideAndNav),
|
||||
_error => this._renderMain(null, {}, fnLoadSideAndNav)
|
||||
);
|
||||
@ -262,7 +262,12 @@ export function Fetch(Base) {
|
||||
|
||||
// Server-Side Rendering
|
||||
if (this.rendered) {
|
||||
const activeEl = getAndActive(this.router, '.sidebar-nav', true, true);
|
||||
const activeEl = this.__getAndActive(
|
||||
this.router,
|
||||
'.sidebar-nav',
|
||||
true,
|
||||
true
|
||||
);
|
||||
if (loadSidebar && activeEl) {
|
||||
activeEl.parentNode.innerHTML += window.__SUB_SIDEBAR__;
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ import * as util from './util/index.js';
|
||||
import * as dom from './util/dom.js';
|
||||
import { Compiler } from './render/compiler.js';
|
||||
import { slugify } from './render/slugify.js';
|
||||
import { get } from './fetch/ajax.js';
|
||||
import { get } from './util/ajax.js';
|
||||
|
||||
// TODO This is deprecated, kept for backwards compatibility. Remove in next
|
||||
// TODO This is deprecated, kept for backwards compatibility. Remove in a
|
||||
// major release. We'll tell people to get everything from the DOCSIFY global
|
||||
// when using the global build, but we'll highly recommend for them to import
|
||||
// from the ESM build (f.e. lib/docsify.esm.js and lib/docsify.min.esm.js).
|
||||
export default function () {
|
||||
export default function initGlobalAPI() {
|
||||
window.Docsify = {
|
||||
util,
|
||||
dom,
|
||||
|
@ -1,8 +1,17 @@
|
||||
import { documentReady } from './util/dom.js';
|
||||
import { Docsify } from './Docsify.js';
|
||||
import initGlobalAPI from './global-api.js';
|
||||
|
||||
// TODO This global API and auto-running Docsify will be deprecated, and removed
|
||||
// in a major release. Instead we'll tell users to use `new Docsify()` to create
|
||||
// and manage their instance(s).
|
||||
|
||||
/**
|
||||
* Global API
|
||||
*/
|
||||
initGlobalAPI();
|
||||
|
||||
/**
|
||||
* Run Docsify
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
documentReady(_ => new Docsify());
|
||||
documentReady(() => new Docsify());
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { noop } from '../util/core.js';
|
||||
|
||||
/** @typedef {import('../Docsify').Constructor} Constructor */
|
||||
/** @typedef {import('../Docsify.js').Constructor} Constructor */
|
||||
|
||||
/**
|
||||
* @template {!Constructor} T
|
||||
@ -8,6 +8,9 @@ import { noop } from '../util/core.js';
|
||||
*/
|
||||
export function Lifecycle(Base) {
|
||||
return class Lifecycle extends Base {
|
||||
_hooks = {};
|
||||
_lifecycle = {};
|
||||
|
||||
initLifecycle() {
|
||||
const hooks = [
|
||||
'init',
|
||||
@ -18,9 +21,6 @@ export function Lifecycle(Base) {
|
||||
'ready',
|
||||
];
|
||||
|
||||
this._hooks = {};
|
||||
this._lifecycle = {};
|
||||
|
||||
hooks.forEach(hook => {
|
||||
const arr = (this._hooks[hook] = []);
|
||||
this._lifecycle[hook] = fn => arr.push(fn);
|
||||
|
@ -8,7 +8,7 @@ import { emojify } from './emojify.js';
|
||||
import {
|
||||
getAndRemoveConfig,
|
||||
removeAtag,
|
||||
getAndRemoveDocisfyIgnorConfig,
|
||||
getAndRemoveDocisfyIgnoreConfig,
|
||||
} from './utils.js';
|
||||
import { imageCompiler } from './compiler/image.js';
|
||||
import { highlightCodeCompiler } from './compiler/code.js';
|
||||
@ -214,7 +214,7 @@ export class Compiler {
|
||||
const nextToc = { level, title: str };
|
||||
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnorConfig(str);
|
||||
getAndRemoveDocisfyIgnoreConfig(str);
|
||||
str = content.trim();
|
||||
|
||||
nextToc.title = removeAtag(str);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
getAndRemoveConfig,
|
||||
removeAtag,
|
||||
getAndRemoveDocisfyIgnorConfig,
|
||||
getAndRemoveDocisfyIgnoreConfig,
|
||||
} from '../utils.js';
|
||||
import { slugify } from './slugify.js';
|
||||
|
||||
@ -11,7 +11,7 @@ export const headingCompiler = ({ renderer, router, _self }) =>
|
||||
const nextToc = { level, title: str };
|
||||
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnorConfig(str);
|
||||
getAndRemoveDocisfyIgnoreConfig(str);
|
||||
str = content.trim();
|
||||
|
||||
nextToc.title = removeAtag(str);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import stripIndent from 'strip-indent';
|
||||
import { get } from '../fetch/ajax.js';
|
||||
import { get } from '../util/ajax.js';
|
||||
|
||||
const cached = {};
|
||||
|
||||
|
@ -1,241 +1,14 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import tinydate from 'tinydate';
|
||||
import * as dom from '../util/dom.js';
|
||||
import { getAndActive, sticky } from '../event/sidebar.js';
|
||||
import { getPath, isAbsolutePath } from '../router/util.js';
|
||||
import { isMobile, inBrowser } from '../util/env.js';
|
||||
import { isPrimitive } from '../util/core.js';
|
||||
import { scrollActiveSidebar } from '../event/scroll.js';
|
||||
import { Compiler } from './compiler.js';
|
||||
import * as tpl from './tpl.js';
|
||||
import { prerenderEmbed } from './embed.js';
|
||||
|
||||
let vueGlobalData;
|
||||
|
||||
function executeScript() {
|
||||
const script = dom
|
||||
.findAll('.markdown-section>script')
|
||||
.filter(s => !/template/.test(s.type))[0];
|
||||
if (!script) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const code = script.innerText.trim();
|
||||
if (!code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
new Function(code)();
|
||||
}
|
||||
|
||||
function formatUpdated(html, updated, fn) {
|
||||
updated =
|
||||
typeof fn === 'function'
|
||||
? fn(updated)
|
||||
: typeof fn === 'string'
|
||||
? tinydate(fn)(new Date(updated))
|
||||
: updated;
|
||||
|
||||
return html.replace(/{docsify-updated}/g, updated);
|
||||
}
|
||||
|
||||
function renderMain(html) {
|
||||
const docsifyConfig = this.config;
|
||||
const markdownElm = dom.find('.markdown-section');
|
||||
const vueVersion =
|
||||
'Vue' in window &&
|
||||
window.Vue.version &&
|
||||
Number(window.Vue.version.charAt(0));
|
||||
|
||||
const isMountedVue = elm => {
|
||||
const isVue2 = Boolean(elm.__vue__ && elm.__vue__._isVue);
|
||||
const isVue3 = Boolean(elm._vnode && elm._vnode.__v_skip);
|
||||
|
||||
return isVue2 || isVue3;
|
||||
};
|
||||
|
||||
if (!html) {
|
||||
html = /* html */ `<h1>404 - Not found</h1>`;
|
||||
}
|
||||
|
||||
if ('Vue' in window) {
|
||||
const mountedElms = dom
|
||||
.findAll('.markdown-section > *')
|
||||
.filter(elm => isMountedVue(elm));
|
||||
|
||||
// Destroy/unmount existing Vue instances
|
||||
for (const mountedElm of mountedElms) {
|
||||
if (vueVersion === 2) {
|
||||
mountedElm.__vue__.$destroy();
|
||||
} else if (vueVersion === 3) {
|
||||
mountedElm.__vue_app__.unmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._renderTo(markdownElm, html);
|
||||
|
||||
// Render sidebar with the TOC
|
||||
!docsifyConfig.loadSidebar && this._renderSidebar();
|
||||
|
||||
// Execute markdown <script>
|
||||
if (
|
||||
docsifyConfig.executeScript ||
|
||||
('Vue' in window && docsifyConfig.executeScript !== false)
|
||||
) {
|
||||
executeScript();
|
||||
}
|
||||
|
||||
// Handle Vue content not mounted by markdown <script>
|
||||
if ('Vue' in window) {
|
||||
const vueMountData = [];
|
||||
const vueComponentNames = Object.keys(docsifyConfig.vueComponents || {});
|
||||
|
||||
// Register global vueComponents
|
||||
if (vueVersion === 2 && vueComponentNames.length) {
|
||||
vueComponentNames.forEach(name => {
|
||||
const isNotRegistered = !window.Vue.options.components[name];
|
||||
|
||||
if (isNotRegistered) {
|
||||
window.Vue.component(name, docsifyConfig.vueComponents[name]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Store global data() return value as shared data object
|
||||
if (
|
||||
!vueGlobalData &&
|
||||
docsifyConfig.vueGlobalOptions &&
|
||||
typeof docsifyConfig.vueGlobalOptions.data === 'function'
|
||||
) {
|
||||
vueGlobalData = docsifyConfig.vueGlobalOptions.data();
|
||||
}
|
||||
|
||||
// vueMounts
|
||||
vueMountData.push(
|
||||
...Object.keys(docsifyConfig.vueMounts || {})
|
||||
.map(cssSelector => [
|
||||
dom.find(markdownElm, cssSelector),
|
||||
docsifyConfig.vueMounts[cssSelector],
|
||||
])
|
||||
.filter(([elm, vueConfig]) => elm)
|
||||
);
|
||||
|
||||
// Template syntax, vueComponents, vueGlobalOptions
|
||||
if (docsifyConfig.vueGlobalOptions || vueComponentNames.length) {
|
||||
const reHasBraces = /{{2}[^{}]*}{2}/;
|
||||
// Matches Vue full and shorthand syntax as attributes in HTML tags.
|
||||
//
|
||||
// Full syntax examples:
|
||||
// v-foo, v-foo[bar], v-foo-bar, v-foo:bar-baz.prop
|
||||
//
|
||||
// Shorthand syntax examples:
|
||||
// @foo, @foo.bar, @foo.bar.baz, @[foo], :foo, :[foo]
|
||||
//
|
||||
// Markup examples:
|
||||
// <div v-html>{{ html }}</div>
|
||||
// <div v-text="msg"></div>
|
||||
// <div v-bind:text-content.prop="text">
|
||||
// <button v-on:click="doThis"></button>
|
||||
// <button v-on:click.once="doThis"></button>
|
||||
// <button v-on:[event]="doThis"></button>
|
||||
// <button @click.stop.prevent="doThis">
|
||||
// <a :href="url">
|
||||
// <a :[key]="url">
|
||||
const reHasDirective = /<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/;
|
||||
|
||||
vueMountData.push(
|
||||
...dom
|
||||
.findAll('.markdown-section > *')
|
||||
// Remove duplicates
|
||||
.filter(elm => !vueMountData.some(([e, c]) => e === elm))
|
||||
// Detect Vue content
|
||||
.filter(elm => {
|
||||
const isVueMount =
|
||||
// is a component
|
||||
elm.tagName.toLowerCase() in
|
||||
(docsifyConfig.vueComponents || {}) ||
|
||||
// has a component(s)
|
||||
elm.querySelector(vueComponentNames.join(',') || null) ||
|
||||
// has curly braces
|
||||
reHasBraces.test(elm.outerHTML) ||
|
||||
// has content directive
|
||||
reHasDirective.test(elm.outerHTML);
|
||||
|
||||
return isVueMount;
|
||||
})
|
||||
.map(elm => {
|
||||
// Clone global configuration
|
||||
const vueConfig = {
|
||||
...docsifyConfig.vueGlobalOptions,
|
||||
};
|
||||
// Replace vueGlobalOptions data() return value with shared data object.
|
||||
// This provides a global store for all Vue instances that receive
|
||||
// vueGlobalOptions as their configuration.
|
||||
if (vueGlobalData) {
|
||||
vueConfig.data = () => vueGlobalData;
|
||||
}
|
||||
|
||||
return [elm, vueConfig];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Mount
|
||||
for (const [mountElm, vueConfig] of vueMountData) {
|
||||
const isVueAttr = 'data-isvue';
|
||||
const isSkipElm =
|
||||
// Is an invalid tag
|
||||
mountElm.matches('pre, script') ||
|
||||
// Is a mounted instance
|
||||
isMountedVue(mountElm) ||
|
||||
// Has mounted instance(s)
|
||||
mountElm.querySelector(`[${isVueAttr}]`);
|
||||
|
||||
if (!isSkipElm) {
|
||||
mountElm.setAttribute(isVueAttr, '');
|
||||
|
||||
if (vueVersion === 2) {
|
||||
vueConfig.el = undefined;
|
||||
new window.Vue(vueConfig).$mount(mountElm);
|
||||
} else if (vueVersion === 3) {
|
||||
const app = window.Vue.createApp(vueConfig);
|
||||
|
||||
// Register global vueComponents
|
||||
vueComponentNames.forEach(name => {
|
||||
const config = docsifyConfig.vueComponents[name];
|
||||
|
||||
app.component(name, config);
|
||||
});
|
||||
|
||||
app.mount(mountElm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderNameLink(vm) {
|
||||
const el = dom.getNode('.app-name-link');
|
||||
const nameLink = vm.config.nameLink;
|
||||
const path = vm.route.path;
|
||||
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPrimitive(vm.config.nameLink)) {
|
||||
el.setAttribute('href', nameLink);
|
||||
} else if (typeof nameLink === 'object') {
|
||||
const match = Object.keys(nameLink).filter(
|
||||
key => path.indexOf(key) > -1
|
||||
)[0];
|
||||
|
||||
el.setAttribute('href', nameLink[match]);
|
||||
}
|
||||
}
|
||||
|
||||
/** @typedef {import('../Docsify').Constructor} Constructor */
|
||||
/** @typedef {import('../Docsify.js').Constructor} Constructor */
|
||||
|
||||
/**
|
||||
* @template {!Constructor} T
|
||||
@ -243,6 +16,232 @@ function renderNameLink(vm) {
|
||||
*/
|
||||
export function Render(Base) {
|
||||
return class Render extends Base {
|
||||
#vueGlobalData;
|
||||
|
||||
#executeScript() {
|
||||
const script = dom
|
||||
.findAll('.markdown-section>script')
|
||||
.filter(s => !/template/.test(s.type))[0];
|
||||
if (!script) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const code = script.innerText.trim();
|
||||
if (!code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
new Function(code)();
|
||||
}
|
||||
|
||||
#formatUpdated(html, updated, fn) {
|
||||
updated =
|
||||
typeof fn === 'function'
|
||||
? fn(updated)
|
||||
: typeof fn === 'string'
|
||||
? tinydate(fn)(new Date(updated))
|
||||
: updated;
|
||||
|
||||
return html.replace(/{docsify-updated}/g, updated);
|
||||
}
|
||||
|
||||
#renderMain(html) {
|
||||
const docsifyConfig = this.config;
|
||||
const markdownElm = dom.find('.markdown-section');
|
||||
const vueVersion =
|
||||
'Vue' in window &&
|
||||
window.Vue.version &&
|
||||
Number(window.Vue.version.charAt(0));
|
||||
|
||||
const isMountedVue = elm => {
|
||||
const isVue2 = Boolean(elm.__vue__ && elm.__vue__._isVue);
|
||||
const isVue3 = Boolean(elm._vnode && elm._vnode.__v_skip);
|
||||
|
||||
return isVue2 || isVue3;
|
||||
};
|
||||
|
||||
if (!html) {
|
||||
html = /* html */ `<h1>404 - Not found</h1>`;
|
||||
}
|
||||
|
||||
if ('Vue' in window) {
|
||||
const mountedElms = dom
|
||||
.findAll('.markdown-section > *')
|
||||
.filter(elm => isMountedVue(elm));
|
||||
|
||||
// Destroy/unmount existing Vue instances
|
||||
for (const mountedElm of mountedElms) {
|
||||
if (vueVersion === 2) {
|
||||
mountedElm.__vue__.$destroy();
|
||||
} else if (vueVersion === 3) {
|
||||
mountedElm.__vue_app__.unmount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._renderTo(markdownElm, html);
|
||||
|
||||
// Render sidebar with the TOC
|
||||
!docsifyConfig.loadSidebar && this._renderSidebar();
|
||||
|
||||
// Execute markdown <script>
|
||||
if (
|
||||
docsifyConfig.executeScript ||
|
||||
('Vue' in window && docsifyConfig.executeScript !== false)
|
||||
) {
|
||||
this.#executeScript();
|
||||
}
|
||||
|
||||
// Handle Vue content not mounted by markdown <script>
|
||||
if ('Vue' in window) {
|
||||
const vueMountData = [];
|
||||
const vueComponentNames = Object.keys(
|
||||
docsifyConfig.vueComponents || {}
|
||||
);
|
||||
|
||||
// Register global vueComponents
|
||||
if (vueVersion === 2 && vueComponentNames.length) {
|
||||
vueComponentNames.forEach(name => {
|
||||
const isNotRegistered = !window.Vue.options.components[name];
|
||||
|
||||
if (isNotRegistered) {
|
||||
window.Vue.component(name, docsifyConfig.vueComponents[name]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Store global data() return value as shared data object
|
||||
if (
|
||||
!this.#vueGlobalData &&
|
||||
docsifyConfig.vueGlobalOptions &&
|
||||
typeof docsifyConfig.vueGlobalOptions.data === 'function'
|
||||
) {
|
||||
this.#vueGlobalData = docsifyConfig.vueGlobalOptions.data();
|
||||
}
|
||||
|
||||
// vueMounts
|
||||
vueMountData.push(
|
||||
...Object.keys(docsifyConfig.vueMounts || {})
|
||||
.map(cssSelector => [
|
||||
dom.find(markdownElm, cssSelector),
|
||||
docsifyConfig.vueMounts[cssSelector],
|
||||
])
|
||||
.filter(([elm, vueConfig]) => elm)
|
||||
);
|
||||
|
||||
// Template syntax, vueComponents, vueGlobalOptions
|
||||
if (docsifyConfig.vueGlobalOptions || vueComponentNames.length) {
|
||||
const reHasBraces = /{{2}[^{}]*}{2}/;
|
||||
// Matches Vue full and shorthand syntax as attributes in HTML tags.
|
||||
//
|
||||
// Full syntax examples:
|
||||
// v-foo, v-foo[bar], v-foo-bar, v-foo:bar-baz.prop
|
||||
//
|
||||
// Shorthand syntax examples:
|
||||
// @foo, @foo.bar, @foo.bar.baz, @[foo], :foo, :[foo]
|
||||
//
|
||||
// Markup examples:
|
||||
// <div v-html>{{ html }}</div>
|
||||
// <div v-text="msg"></div>
|
||||
// <div v-bind:text-content.prop="text">
|
||||
// <button v-on:click="doThis"></button>
|
||||
// <button v-on:click.once="doThis"></button>
|
||||
// <button v-on:[event]="doThis"></button>
|
||||
// <button @click.stop.prevent="doThis">
|
||||
// <a :href="url">
|
||||
// <a :[key]="url">
|
||||
const reHasDirective = /<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/;
|
||||
|
||||
vueMountData.push(
|
||||
...dom
|
||||
.findAll('.markdown-section > *')
|
||||
// Remove duplicates
|
||||
.filter(elm => !vueMountData.some(([e, c]) => e === elm))
|
||||
// Detect Vue content
|
||||
.filter(elm => {
|
||||
const isVueMount =
|
||||
// is a component
|
||||
elm.tagName.toLowerCase() in
|
||||
(docsifyConfig.vueComponents || {}) ||
|
||||
// has a component(s)
|
||||
elm.querySelector(vueComponentNames.join(',') || null) ||
|
||||
// has curly braces
|
||||
reHasBraces.test(elm.outerHTML) ||
|
||||
// has content directive
|
||||
reHasDirective.test(elm.outerHTML);
|
||||
|
||||
return isVueMount;
|
||||
})
|
||||
.map(elm => {
|
||||
// Clone global configuration
|
||||
const vueConfig = {
|
||||
...docsifyConfig.vueGlobalOptions,
|
||||
};
|
||||
// Replace vueGlobalOptions data() return value with shared data object.
|
||||
// This provides a global store for all Vue instances that receive
|
||||
// vueGlobalOptions as their configuration.
|
||||
if (this.#vueGlobalData) {
|
||||
vueConfig.data = () => this.#vueGlobalData;
|
||||
}
|
||||
|
||||
return [elm, vueConfig];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Mount
|
||||
for (const [mountElm, vueConfig] of vueMountData) {
|
||||
const isVueAttr = 'data-isvue';
|
||||
const isSkipElm =
|
||||
// Is an invalid tag
|
||||
mountElm.matches('pre, script') ||
|
||||
// Is a mounted instance
|
||||
isMountedVue(mountElm) ||
|
||||
// Has mounted instance(s)
|
||||
mountElm.querySelector(`[${isVueAttr}]`);
|
||||
|
||||
if (!isSkipElm) {
|
||||
mountElm.setAttribute(isVueAttr, '');
|
||||
|
||||
if (vueVersion === 2) {
|
||||
vueConfig.el = undefined;
|
||||
new window.Vue(vueConfig).$mount(mountElm);
|
||||
} else if (vueVersion === 3) {
|
||||
const app = window.Vue.createApp(vueConfig);
|
||||
|
||||
// Register global vueComponents
|
||||
vueComponentNames.forEach(name => {
|
||||
const config = docsifyConfig.vueComponents[name];
|
||||
|
||||
app.component(name, config);
|
||||
});
|
||||
|
||||
app.mount(mountElm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#renderNameLink(vm) {
|
||||
const el = dom.getNode('.app-name-link');
|
||||
const nameLink = vm.config.nameLink;
|
||||
const path = vm.route.path;
|
||||
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPrimitive(vm.config.nameLink)) {
|
||||
el.setAttribute('href', nameLink);
|
||||
} else if (typeof nameLink === 'object') {
|
||||
const match = Object.keys(nameLink).filter(
|
||||
key => path.indexOf(key) > -1
|
||||
)[0];
|
||||
|
||||
el.setAttribute('href', nameLink[match]);
|
||||
}
|
||||
}
|
||||
_renderTo(el, content, replace) {
|
||||
const node = dom.getNode(el);
|
||||
if (node) {
|
||||
@ -269,7 +268,12 @@ export function Render(Base) {
|
||||
}
|
||||
|
||||
this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel));
|
||||
const activeEl = getAndActive(this.router, '.sidebar-nav', true, true);
|
||||
const activeEl = this.__getAndActive(
|
||||
this.router,
|
||||
'.sidebar-nav',
|
||||
true,
|
||||
true
|
||||
);
|
||||
if (loadSidebar && activeEl) {
|
||||
activeEl.parentNode.innerHTML +=
|
||||
this.compiler.subSidebar(subMaxLevel) || '';
|
||||
@ -285,7 +289,7 @@ export function Render(Base) {
|
||||
_bindEventOnRendered(activeEl) {
|
||||
const { autoHeader } = this.config;
|
||||
|
||||
scrollActiveSidebar(this.router);
|
||||
this.__scrollActiveSidebar(this.router);
|
||||
|
||||
if (autoHeader && activeEl) {
|
||||
const main = dom.getNode('#main');
|
||||
@ -301,20 +305,20 @@ export function Render(Base) {
|
||||
_renderNav(text) {
|
||||
text && this._renderTo('nav', this.compiler.compile(text));
|
||||
if (this.config.loadNavbar) {
|
||||
getAndActive(this.router, 'nav');
|
||||
this.__getAndActive(this.router, 'nav');
|
||||
}
|
||||
}
|
||||
|
||||
_renderMain(text, opt = {}, next) {
|
||||
if (!text) {
|
||||
return renderMain.call(this, text);
|
||||
return this.#renderMain(text);
|
||||
}
|
||||
|
||||
this.callHook('beforeEach', text, result => {
|
||||
let html;
|
||||
const callback = () => {
|
||||
if (opt.updatedAt) {
|
||||
html = formatUpdated(
|
||||
html = this.#formatUpdated(
|
||||
html,
|
||||
opt.updatedAt,
|
||||
this.config.formatUpdated
|
||||
@ -322,7 +326,7 @@ export function Render(Base) {
|
||||
}
|
||||
|
||||
this.callHook('afterEach', html, hookData => {
|
||||
renderMain.call(this, hookData);
|
||||
this.#renderMain(hookData);
|
||||
next();
|
||||
});
|
||||
};
|
||||
@ -386,12 +390,12 @@ export function Render(Base) {
|
||||
}
|
||||
|
||||
this._renderTo('.cover-main', html);
|
||||
sticky();
|
||||
this.__sticky();
|
||||
}
|
||||
|
||||
_updateRender() {
|
||||
// Render name link
|
||||
renderNameLink(this);
|
||||
this.#renderNameLink(this);
|
||||
}
|
||||
|
||||
initRender() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
*
|
||||
* @param {string} str The string to parse.
|
||||
*
|
||||
* @return {object} The original string and parsed object, { str, config }.
|
||||
* @return {{str: string, config: object}} The original string formatted, and parsed object, { str, config }.
|
||||
*/
|
||||
export function getAndRemoveConfig(str = '') {
|
||||
const config = {};
|
||||
@ -43,7 +43,7 @@ export function getAndRemoveConfig(str = '') {
|
||||
* Remove the <a> tag from sidebar when the header with link, details see issue 1069
|
||||
* @param {string} str The string to deal with.
|
||||
*
|
||||
* @return {string} str The string after delete the <a> element.
|
||||
* @return {string} The string after delete the <a> element.
|
||||
*/
|
||||
export function removeAtag(str = '') {
|
||||
return str.replace(/(<\/?a.*?>)/gi, '');
|
||||
@ -51,11 +51,11 @@ export function removeAtag(str = '') {
|
||||
|
||||
/**
|
||||
* Remove the docsifyIgnore configs and return the str
|
||||
* @param {string} str The string to deal with.
|
||||
* @param {string} content The string to deal with.
|
||||
*
|
||||
* @return {string} str The string after delete the docsifyIgnore configs.
|
||||
* @return {{content: string, ignoreAllSubs: boolean, ignoreSubHeading: boolean}} The string after delete the docsifyIgnore configs, and whether to ignore some or all.
|
||||
*/
|
||||
export function getAndRemoveDocisfyIgnorConfig(content = '') {
|
||||
export function getAndRemoveDocisfyIgnoreConfig(content = '') {
|
||||
let ignoreAllSubs, ignoreSubHeading;
|
||||
if (/<!-- {docsify-ignore} -->/g.test(content)) {
|
||||
content = content.replace('<!-- {docsify-ignore} -->', '');
|
||||
|
@ -8,32 +8,37 @@ import {
|
||||
} from '../util.js';
|
||||
import { noop } from '../../util/core.js';
|
||||
|
||||
const cached = {};
|
||||
|
||||
function getAlias(path, alias, last) {
|
||||
const match = Object.keys(alias).filter(key => {
|
||||
const re = cached[key] || (cached[key] = new RegExp(`^${key}$`));
|
||||
return re.test(path) && path !== last;
|
||||
})[0];
|
||||
|
||||
return match
|
||||
? getAlias(path.replace(cached[match], alias[match]), alias, path)
|
||||
: path;
|
||||
}
|
||||
|
||||
function getFileName(path, ext) {
|
||||
return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path)
|
||||
? path
|
||||
: /\/$/g.test(path)
|
||||
? `${path}README${ext}`
|
||||
: `${path}${ext}`;
|
||||
}
|
||||
|
||||
export class History {
|
||||
#cached = {};
|
||||
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
#getAlias(path, alias, last) {
|
||||
const match = Object.keys(alias).filter(key => {
|
||||
const re =
|
||||
this.#cached[key] || (this.#cached[key] = new RegExp(`^${key}$`));
|
||||
return re.test(path) && path !== last;
|
||||
})[0];
|
||||
|
||||
return match
|
||||
? this.#getAlias(
|
||||
path.replace(this.#cached[match], alias[match]),
|
||||
alias,
|
||||
path
|
||||
)
|
||||
: path;
|
||||
}
|
||||
|
||||
#getFileName(path, ext) {
|
||||
return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path)
|
||||
? path
|
||||
: /\/$/g.test(path)
|
||||
? `${path}README${ext}`
|
||||
: `${path}${ext}`;
|
||||
}
|
||||
|
||||
getBasePath() {
|
||||
return this.config.basePath;
|
||||
}
|
||||
@ -43,8 +48,8 @@ export class History {
|
||||
const base = this.getBasePath();
|
||||
const ext = typeof config.ext === 'string' ? config.ext : '.md';
|
||||
|
||||
path = config.alias ? getAlias(path, config.alias) : path;
|
||||
path = getFileName(path, ext);
|
||||
path = config.alias ? this.#getAlias(path, config.alias) : path;
|
||||
path = this.#getFileName(path, ext);
|
||||
path = path === `/README${ext}` ? config.homepage || path : path;
|
||||
path = isAbsolutePath(path) ? path : getPath(base, path);
|
||||
|
||||
|
@ -7,11 +7,9 @@ function replaceHash(path) {
|
||||
const i = location.href.indexOf('#');
|
||||
location.replace(location.href.slice(0, i >= 0 ? i : 0) + '#' + path);
|
||||
}
|
||||
|
||||
export class HashHistory extends History {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
this.mode = 'hash';
|
||||
}
|
||||
mode = 'hash';
|
||||
|
||||
getBasePath() {
|
||||
const path = window.location.pathname || '';
|
||||
|
@ -4,10 +4,7 @@ import { parseQuery, getPath } from '../util.js';
|
||||
import { History } from './base.js';
|
||||
|
||||
export class HTML5History extends History {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
this.mode = 'history';
|
||||
}
|
||||
mode = 'history';
|
||||
|
||||
getCurrentPath() {
|
||||
const base = this.getBasePath();
|
||||
|
@ -12,7 +12,7 @@ import { HTML5History } from './history/html5.js';
|
||||
/** @type {Route} */
|
||||
let lastRoute = {};
|
||||
|
||||
/** @typedef {import('../Docsify').Constructor} Constructor */
|
||||
/** @typedef {import('../Docsify.js').Constructor} Constructor */
|
||||
|
||||
/**
|
||||
* @template {!Constructor} T
|
||||
@ -20,12 +20,7 @@ let lastRoute = {};
|
||||
*/
|
||||
export function Router(Base) {
|
||||
return class Router extends Base {
|
||||
/** @param {any[]} args */
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
|
||||
this.route = {};
|
||||
}
|
||||
route = {};
|
||||
|
||||
updateRender() {
|
||||
this.router.normalize();
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @ts-check
|
||||
/* eslint-disable no-unused-vars */
|
||||
import progressbar from '../render/progressbar.js';
|
||||
import { noop } from '../util/core.js';
|
||||
import { noop } from './core.js';
|
||||
|
||||
/** @typedef {{updatedAt: string}} CacheOpt */
|
||||
|
@ -1,8 +0,0 @@
|
||||
export function removeDocsifyIgnoreTag(str) {
|
||||
return str
|
||||
.replace(/<!-- {docsify-ignore} -->/, '')
|
||||
.replace(/{docsify-ignore}/, '')
|
||||
.replace(/<!-- {docsify-ignore-all} -->/, '')
|
||||
.replace(/{docsify-ignore-all}/, '')
|
||||
.trim();
|
||||
}
|
@ -1,12 +1,30 @@
|
||||
import { makeExactMatcher } from './exact-match.js';
|
||||
import { createNextFunction } from './next.js';
|
||||
|
||||
/** @typedef {import('../Docsify').Constructor} Constructor */
|
||||
/** @typedef {import('../Docsify.js').Constructor} Constructor */
|
||||
|
||||
/** @typedef {Record<string, string | VirtualRouteHandler>} VirtualRoutesMap */
|
||||
/** @typedef {(route: string, match: RegExpMatchArray | null) => string | void | Promise<string | void> } VirtualRouteHandler */
|
||||
|
||||
/**
|
||||
* Allows users/plugins to introduce dynamically created content into their docsify
|
||||
* websites. https://github.com/docsifyjs/docsify/issues/1737
|
||||
*
|
||||
* For instance:
|
||||
*
|
||||
* ```js
|
||||
* window.$docsify = {
|
||||
* routes: {
|
||||
* '/items/(.+)': function (route, matched) {
|
||||
* return `
|
||||
* # Item Page: ${matched[1]}
|
||||
* This is an item
|
||||
* `;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @template {!Constructor} T
|
||||
* @param {T} Base - The class to extend
|
||||
*/
|
||||
|
@ -1,4 +1,5 @@
|
||||
/** @typedef {((value: any) => void) => void} OnNext */
|
||||
/** @typedef {(value: any) => void} CB */
|
||||
/** @typedef {(cb: CB) => void} OnNext */
|
||||
/** @typedef {(value: any) => void} NextFunction */
|
||||
|
||||
/**
|
||||
@ -7,6 +8,7 @@
|
||||
* @returns {[NextFunction, OnNext]}
|
||||
*/
|
||||
export function createNextFunction() {
|
||||
/** @type {CB} */
|
||||
let storedCb = () => null;
|
||||
|
||||
function next(value) {
|
||||
|
@ -2,9 +2,9 @@
|
||||
* Fork https://github.com/egoist/docute/blob/master/src/utils/front-matter.js
|
||||
*/
|
||||
/* eslint-disable */
|
||||
import parser from './yaml.js'
|
||||
import parser from './yaml.js';
|
||||
|
||||
const optionalByteOrderMark = '\\ufeff?'
|
||||
const optionalByteOrderMark = '\\ufeff?';
|
||||
const pattern =
|
||||
'^(' +
|
||||
optionalByteOrderMark +
|
||||
@ -13,37 +13,37 @@ const pattern =
|
||||
'(?:\\2|\\.\\.\\.)' +
|
||||
'$' +
|
||||
'' +
|
||||
'(?:\\n)?)'
|
||||
'(?:\\n)?)';
|
||||
// NOTE: If this pattern uses the 'g' flag the `regex` variable definition will
|
||||
// need to be moved down into the functions that use it.
|
||||
const regex = new RegExp(pattern, 'm')
|
||||
const regex = new RegExp(pattern, 'm');
|
||||
|
||||
function extractor(string) {
|
||||
string = string || ''
|
||||
string = string || '';
|
||||
|
||||
const lines = string.split(/(\r?\n)/)
|
||||
const lines = string.split(/(\r?\n)/);
|
||||
if (lines[0] && /= yaml =|---/.test(lines[0])) {
|
||||
return parse(string)
|
||||
return parse(string);
|
||||
} else {
|
||||
return { attributes: {}, body: string }
|
||||
return { attributes: {}, body: string };
|
||||
}
|
||||
}
|
||||
|
||||
function parse(string) {
|
||||
const match = regex.exec(string)
|
||||
const match = regex.exec(string);
|
||||
|
||||
if (!match) {
|
||||
return {
|
||||
attributes: {},
|
||||
body: string
|
||||
}
|
||||
body: string,
|
||||
};
|
||||
}
|
||||
|
||||
const yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '')
|
||||
const attributes = parser(yaml) || {}
|
||||
const body = string.replace(match[0], '')
|
||||
const yaml = match[match.length - 1].replace(/^\s+|\s+$/g, '');
|
||||
const attributes = parser(yaml) || {};
|
||||
const body = string.replace(match[0], '');
|
||||
|
||||
return { attributes: attributes, body: body, frontmatter: yaml }
|
||||
return { attributes: attributes, body: body, frontmatter: yaml };
|
||||
}
|
||||
|
||||
export default extractor
|
||||
export default extractor;
|
||||
|
@ -27,7 +27,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
/**
|
||||
* @name YAML
|
||||
* @namespace
|
||||
*/
|
||||
*/
|
||||
|
||||
var errors = [],
|
||||
reference_blocks = [],
|
||||
@ -46,14 +46,16 @@ var errors = [],
|
||||
key: new RegExp('([a-z0-9_-][ a-z0-9_-]+):( .+)?', 'i'),
|
||||
item: new RegExp('^-\\s+'),
|
||||
trim: new RegExp('^\\s+|\\s+$'),
|
||||
comment: new RegExp('([^\\\'\\"#]+([\\\'\\"][^\\\'\\"]*[\\\'\\"])*)*(#.*)?')
|
||||
}
|
||||
comment: new RegExp(
|
||||
'([^\\\'\\"#]+([\\\'\\"][^\\\'\\"]*[\\\'\\"])*)*(#.*)?'
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* @class A block of lines of a given level.
|
||||
* @param {int} lvl The block's level.
|
||||
* @private
|
||||
*/
|
||||
* @class A block of lines of a given level.
|
||||
* @param {int} lvl The block's level.
|
||||
* @private
|
||||
*/
|
||||
function Block(lvl) {
|
||||
return {
|
||||
/* The block's parent */
|
||||
@ -67,339 +69,339 @@ function Block(lvl) {
|
||||
/* Blocks with greater level */
|
||||
children: [],
|
||||
/* Add a block to the children collection */
|
||||
addChild: function(obj) {
|
||||
this.children.push(obj)
|
||||
obj.parent = this
|
||||
++this.length
|
||||
}
|
||||
}
|
||||
addChild: function (obj) {
|
||||
this.children.push(obj);
|
||||
obj.parent = this;
|
||||
++this.length;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function parser(str) {
|
||||
var regLevel = regex['regLevel']
|
||||
var invalidLine = regex['invalidLine']
|
||||
var lines = str.split('\n')
|
||||
var m
|
||||
var regLevel = regex['regLevel'];
|
||||
var invalidLine = regex['invalidLine'];
|
||||
var lines = str.split('\n');
|
||||
var m;
|
||||
var level = 0,
|
||||
curLevel = 0
|
||||
curLevel = 0;
|
||||
|
||||
var blocks = []
|
||||
var blocks = [];
|
||||
|
||||
var result = new Block(-1)
|
||||
var currentBlock = new Block(0)
|
||||
result.addChild(currentBlock)
|
||||
var levels = []
|
||||
var line = ''
|
||||
var result = new Block(-1);
|
||||
var currentBlock = new Block(0);
|
||||
result.addChild(currentBlock);
|
||||
var levels = [];
|
||||
var line = '';
|
||||
|
||||
blocks.push(currentBlock)
|
||||
levels.push(level)
|
||||
blocks.push(currentBlock);
|
||||
levels.push(level);
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; ++i) {
|
||||
line = lines[i]
|
||||
line = lines[i];
|
||||
|
||||
if (line.match(invalidLine)) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((m = regLevel.exec(line))) {
|
||||
level = m[1].length
|
||||
} else level = 0
|
||||
level = m[1].length;
|
||||
} else level = 0;
|
||||
|
||||
if (level > curLevel) {
|
||||
var oldBlock = currentBlock
|
||||
currentBlock = new Block(level)
|
||||
oldBlock.addChild(currentBlock)
|
||||
blocks.push(currentBlock)
|
||||
levels.push(level)
|
||||
var oldBlock = currentBlock;
|
||||
currentBlock = new Block(level);
|
||||
oldBlock.addChild(currentBlock);
|
||||
blocks.push(currentBlock);
|
||||
levels.push(level);
|
||||
} else if (level < curLevel) {
|
||||
var added = false
|
||||
var added = false;
|
||||
|
||||
var k = levels.length - 1
|
||||
var k = levels.length - 1;
|
||||
for (; k >= 0; --k) {
|
||||
if (levels[k] == level) {
|
||||
currentBlock = new Block(level)
|
||||
blocks.push(currentBlock)
|
||||
levels.push(level)
|
||||
if (blocks[k].parent != null) blocks[k].parent.addChild(currentBlock)
|
||||
added = true
|
||||
break
|
||||
currentBlock = new Block(level);
|
||||
blocks.push(currentBlock);
|
||||
levels.push(level);
|
||||
if (blocks[k].parent != null) blocks[k].parent.addChild(currentBlock);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
errors.push('Error: Invalid indentation at line ' + i + ': ' + line)
|
||||
return
|
||||
errors.push('Error: Invalid indentation at line ' + i + ': ' + line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currentBlock.lines.push(line.replace(regex['trim'], ''))
|
||||
curLevel = level
|
||||
currentBlock.lines.push(line.replace(regex['trim'], ''));
|
||||
curLevel = level;
|
||||
}
|
||||
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
function processValue(val) {
|
||||
val = val.replace(regex['trim'], '')
|
||||
var m = null
|
||||
val = val.replace(regex['trim'], '');
|
||||
var m = null;
|
||||
|
||||
if (val == 'true') {
|
||||
return true
|
||||
return true;
|
||||
} else if (val == 'false') {
|
||||
return false
|
||||
return false;
|
||||
} else if (val == '.NaN') {
|
||||
return Number.NaN
|
||||
return Number.NaN;
|
||||
} else if (val == 'null') {
|
||||
return null
|
||||
return null;
|
||||
} else if (val == '.inf') {
|
||||
return Number.POSITIVE_INFINITY
|
||||
return Number.POSITIVE_INFINITY;
|
||||
} else if (val == '-.inf') {
|
||||
return Number.NEGATIVE_INFINITY
|
||||
return Number.NEGATIVE_INFINITY;
|
||||
} else if ((m = val.match(regex['dashesString']))) {
|
||||
return m[1]
|
||||
return m[1];
|
||||
} else if ((m = val.match(regex['quotesString']))) {
|
||||
return m[1]
|
||||
return m[1];
|
||||
} else if ((m = val.match(regex['float']))) {
|
||||
return parseFloat(m[0])
|
||||
return parseFloat(m[0]);
|
||||
} else if ((m = val.match(regex['integer']))) {
|
||||
return parseInt(m[0])
|
||||
return parseInt(m[0]);
|
||||
} else if (!isNaN((m = Date.parse(val)))) {
|
||||
return new Date(m)
|
||||
return new Date(m);
|
||||
} else if ((m = val.match(regex['single_key_value']))) {
|
||||
var res = {}
|
||||
res[m[1]] = processValue(m[2])
|
||||
return res
|
||||
var res = {};
|
||||
res[m[1]] = processValue(m[2]);
|
||||
return res;
|
||||
} else if ((m = val.match(regex['array']))) {
|
||||
var count = 0,
|
||||
c = ' '
|
||||
var res = []
|
||||
var content = ''
|
||||
var str = false
|
||||
c = ' ';
|
||||
var res = [];
|
||||
var content = '';
|
||||
var str = false;
|
||||
for (var j = 0, lenJ = m[1].length; j < lenJ; ++j) {
|
||||
c = m[1][j]
|
||||
c = m[1][j];
|
||||
if (c == "'" || c == '"') {
|
||||
if (str === false) {
|
||||
str = c
|
||||
content += c
|
||||
continue
|
||||
str = c;
|
||||
content += c;
|
||||
continue;
|
||||
} else if ((c == "'" && str == "'") || (c == '"' && str == '"')) {
|
||||
str = false
|
||||
content += c
|
||||
continue
|
||||
str = false;
|
||||
content += c;
|
||||
continue;
|
||||
}
|
||||
} else if (str === false && (c == '[' || c == '{')) {
|
||||
++count
|
||||
++count;
|
||||
} else if (str === false && (c == ']' || c == '}')) {
|
||||
--count
|
||||
--count;
|
||||
} else if (str === false && count == 0 && c == ',') {
|
||||
res.push(processValue(content))
|
||||
content = ''
|
||||
continue
|
||||
res.push(processValue(content));
|
||||
content = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
content += c
|
||||
content += c;
|
||||
}
|
||||
|
||||
if (content.length > 0) res.push(processValue(content))
|
||||
return res
|
||||
if (content.length > 0) res.push(processValue(content));
|
||||
return res;
|
||||
} else if ((m = val.match(regex['map']))) {
|
||||
var count = 0,
|
||||
c = ' '
|
||||
var res = []
|
||||
var content = ''
|
||||
var str = false
|
||||
c = ' ';
|
||||
var res = [];
|
||||
var content = '';
|
||||
var str = false;
|
||||
for (var j = 0, lenJ = m[1].length; j < lenJ; ++j) {
|
||||
c = m[1][j]
|
||||
c = m[1][j];
|
||||
if (c == "'" || c == '"') {
|
||||
if (str === false) {
|
||||
str = c
|
||||
content += c
|
||||
continue
|
||||
str = c;
|
||||
content += c;
|
||||
continue;
|
||||
} else if ((c == "'" && str == "'") || (c == '"' && str == '"')) {
|
||||
str = false
|
||||
content += c
|
||||
continue
|
||||
str = false;
|
||||
content += c;
|
||||
continue;
|
||||
}
|
||||
} else if (str === false && (c == '[' || c == '{')) {
|
||||
++count
|
||||
++count;
|
||||
} else if (str === false && (c == ']' || c == '}')) {
|
||||
--count
|
||||
--count;
|
||||
} else if (str === false && count == 0 && c == ',') {
|
||||
res.push(content)
|
||||
content = ''
|
||||
continue
|
||||
res.push(content);
|
||||
content = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
content += c
|
||||
content += c;
|
||||
}
|
||||
|
||||
if (content.length > 0) res.push(content)
|
||||
if (content.length > 0) res.push(content);
|
||||
|
||||
var newRes = {}
|
||||
var newRes = {};
|
||||
for (var j = 0, lenJ = res.length; j < lenJ; ++j) {
|
||||
if ((m = res[j].match(regex['key_value']))) {
|
||||
newRes[m[1]] = processValue(m[2])
|
||||
newRes[m[1]] = processValue(m[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return newRes
|
||||
} else return val
|
||||
return newRes;
|
||||
} else return val;
|
||||
}
|
||||
|
||||
function processFoldedBlock(block) {
|
||||
var lines = block.lines
|
||||
var children = block.children
|
||||
var str = lines.join(' ')
|
||||
var chunks = [str]
|
||||
var lines = block.lines;
|
||||
var children = block.children;
|
||||
var str = lines.join(' ');
|
||||
var chunks = [str];
|
||||
for (var i = 0, len = children.length; i < len; ++i) {
|
||||
chunks.push(processFoldedBlock(children[i]))
|
||||
chunks.push(processFoldedBlock(children[i]));
|
||||
}
|
||||
return chunks.join('\n')
|
||||
return chunks.join('\n');
|
||||
}
|
||||
|
||||
function processLiteralBlock(block) {
|
||||
var lines = block.lines
|
||||
var children = block.children
|
||||
var str = lines.join('\n')
|
||||
var lines = block.lines;
|
||||
var children = block.children;
|
||||
var str = lines.join('\n');
|
||||
for (var i = 0, len = children.length; i < len; ++i) {
|
||||
str += processLiteralBlock(children[i])
|
||||
str += processLiteralBlock(children[i]);
|
||||
}
|
||||
return str
|
||||
return str;
|
||||
}
|
||||
|
||||
function processBlock(blocks) {
|
||||
var m = null
|
||||
var res = {}
|
||||
var lines = null
|
||||
var children = null
|
||||
var currentObj = null
|
||||
var m = null;
|
||||
var res = {};
|
||||
var lines = null;
|
||||
var children = null;
|
||||
var currentObj = null;
|
||||
|
||||
var level = -1
|
||||
var level = -1;
|
||||
|
||||
var processedBlocks = []
|
||||
var processedBlocks = [];
|
||||
|
||||
var isMap = true
|
||||
var isMap = true;
|
||||
|
||||
for (var j = 0, lenJ = blocks.length; j < lenJ; ++j) {
|
||||
if (level != -1 && level != blocks[j].level) continue
|
||||
if (level != -1 && level != blocks[j].level) continue;
|
||||
|
||||
processedBlocks.push(j)
|
||||
processedBlocks.push(j);
|
||||
|
||||
level = blocks[j].level
|
||||
lines = blocks[j].lines
|
||||
children = blocks[j].children
|
||||
currentObj = null
|
||||
level = blocks[j].level;
|
||||
lines = blocks[j].lines;
|
||||
children = blocks[j].children;
|
||||
currentObj = null;
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; ++i) {
|
||||
var line = lines[i]
|
||||
var line = lines[i];
|
||||
|
||||
if ((m = line.match(regex['key']))) {
|
||||
var key = m[1]
|
||||
var key = m[1];
|
||||
|
||||
if (key[0] == '-') {
|
||||
key = key.replace(regex['item'], '')
|
||||
key = key.replace(regex['item'], '');
|
||||
if (isMap) {
|
||||
isMap = false
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = []
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
if (currentObj != null) res.push(currentObj)
|
||||
currentObj = {}
|
||||
isMap = true
|
||||
if (currentObj != null) res.push(currentObj);
|
||||
currentObj = {};
|
||||
isMap = true;
|
||||
}
|
||||
|
||||
if (typeof m[2] != 'undefined') {
|
||||
var value = m[2].replace(regex['trim'], '')
|
||||
var value = m[2].replace(regex['trim'], '');
|
||||
if (value[0] == '&') {
|
||||
var nb = processBlock(children)
|
||||
if (currentObj != null) currentObj[key] = nb
|
||||
else res[key] = nb
|
||||
reference_blocks[value.substr(1)] = nb
|
||||
var nb = processBlock(children);
|
||||
if (currentObj != null) currentObj[key] = nb;
|
||||
else res[key] = nb;
|
||||
reference_blocks[value.substr(1)] = nb;
|
||||
} else if (value[0] == '|') {
|
||||
if (currentObj != null)
|
||||
currentObj[key] = processLiteralBlock(children.shift())
|
||||
else res[key] = processLiteralBlock(children.shift())
|
||||
currentObj[key] = processLiteralBlock(children.shift());
|
||||
else res[key] = processLiteralBlock(children.shift());
|
||||
} else if (value[0] == '*') {
|
||||
var v = value.substr(1)
|
||||
var no = {}
|
||||
var v = value.substr(1);
|
||||
var no = {};
|
||||
|
||||
if (typeof reference_blocks[v] == 'undefined') {
|
||||
errors.push("Reference '" + v + "' not found!")
|
||||
errors.push("Reference '" + v + "' not found!");
|
||||
} else {
|
||||
for (var k in reference_blocks[v]) {
|
||||
no[k] = reference_blocks[v][k]
|
||||
no[k] = reference_blocks[v][k];
|
||||
}
|
||||
|
||||
if (currentObj != null) currentObj[key] = no
|
||||
else res[key] = no
|
||||
if (currentObj != null) currentObj[key] = no;
|
||||
else res[key] = no;
|
||||
}
|
||||
} else if (value[0] == '>') {
|
||||
if (currentObj != null)
|
||||
currentObj[key] = processFoldedBlock(children.shift())
|
||||
else res[key] = processFoldedBlock(children.shift())
|
||||
currentObj[key] = processFoldedBlock(children.shift());
|
||||
else res[key] = processFoldedBlock(children.shift());
|
||||
} else {
|
||||
if (currentObj != null) currentObj[key] = processValue(value)
|
||||
else res[key] = processValue(value)
|
||||
if (currentObj != null) currentObj[key] = processValue(value);
|
||||
else res[key] = processValue(value);
|
||||
}
|
||||
} else {
|
||||
if (currentObj != null) currentObj[key] = processBlock(children)
|
||||
else res[key] = processBlock(children)
|
||||
if (currentObj != null) currentObj[key] = processBlock(children);
|
||||
else res[key] = processBlock(children);
|
||||
}
|
||||
} else if (line.match(/^-\s*$/)) {
|
||||
if (isMap) {
|
||||
isMap = false
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = []
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
if (currentObj != null) res.push(currentObj)
|
||||
currentObj = {}
|
||||
isMap = true
|
||||
continue
|
||||
if (currentObj != null) res.push(currentObj);
|
||||
currentObj = {};
|
||||
isMap = true;
|
||||
continue;
|
||||
} else if ((m = line.match(/^-\s*(.*)/))) {
|
||||
if (currentObj != null) currentObj.push(processValue(m[1]))
|
||||
if (currentObj != null) currentObj.push(processValue(m[1]));
|
||||
else {
|
||||
if (isMap) {
|
||||
isMap = false
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = []
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
res.push(processValue(m[1]))
|
||||
res.push(processValue(m[1]));
|
||||
}
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentObj != null) {
|
||||
if (isMap) {
|
||||
isMap = false
|
||||
isMap = false;
|
||||
if (typeof res.length === 'undefined') {
|
||||
res = []
|
||||
res = [];
|
||||
}
|
||||
}
|
||||
res.push(currentObj)
|
||||
res.push(currentObj);
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = processedBlocks.length - 1; j >= 0; --j) {
|
||||
blocks.splice.call(blocks, processedBlocks[j], 1)
|
||||
blocks.splice.call(blocks, processedBlocks[j], 1);
|
||||
}
|
||||
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
|
||||
function semanticAnalysis(blocks) {
|
||||
var res = processBlock(blocks.children)
|
||||
return res
|
||||
var res = processBlock(blocks.children);
|
||||
return res;
|
||||
}
|
||||
|
||||
function preProcess(src) {
|
||||
var m
|
||||
var lines = src.split('\n')
|
||||
var m;
|
||||
var lines = src.split('\n');
|
||||
|
||||
var r = regex['comment']
|
||||
var r = regex['comment'];
|
||||
|
||||
for (var i in lines) {
|
||||
if ((m = lines[i].match(r))) {
|
||||
@ -412,24 +414,24 @@ function preProcess(src) {
|
||||
lines[i] = "";
|
||||
*/
|
||||
if (typeof m[3] !== 'undefined') {
|
||||
lines[i] = m[0].substr(0, m[0].length - m[3].length)
|
||||
lines[i] = m[0].substr(0, m[0].length - m[3].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function load(str) {
|
||||
errors = []
|
||||
reference_blocks = []
|
||||
processing_time = new Date().getTime()
|
||||
var pre = preProcess(str)
|
||||
var doc = parser(pre)
|
||||
var res = semanticAnalysis(doc)
|
||||
processing_time = new Date().getTime() - processing_time
|
||||
errors = [];
|
||||
reference_blocks = [];
|
||||
processing_time = new Date().getTime();
|
||||
var pre = preProcess(str);
|
||||
var doc = parser(pre);
|
||||
var res = semanticAnalysis(doc);
|
||||
processing_time = new Date().getTime() - processing_time;
|
||||
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
|
||||
export default load
|
||||
export default load;
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import { getAndRemoveConfig } from '../../core/render/utils.js';
|
||||
import { removeDocsifyIgnoreTag } from '../../core/util/str.js';
|
||||
import {
|
||||
getAndRemoveConfig,
|
||||
getAndRemoveDocisfyIgnoreConfig,
|
||||
} from '../../core/render/utils.js';
|
||||
|
||||
let INDEXS = {};
|
||||
|
||||
@ -88,7 +90,7 @@ export function genIndex(path, content = '', router, depth) {
|
||||
if (token.type === 'heading' && token.depth <= depth) {
|
||||
const { str, config } = getAndRemoveConfig(token.text);
|
||||
|
||||
const text = removeDocsifyIgnoreTag(token.text);
|
||||
const text = getAndRemoveDocisfyIgnoreConfig(token.text).content;
|
||||
|
||||
if (config.id) {
|
||||
slug = router.toURL(path, { id: slugify(config.id) });
|
||||
@ -97,7 +99,7 @@ export function genIndex(path, content = '', router, depth) {
|
||||
}
|
||||
|
||||
if (str) {
|
||||
title = removeDocsifyIgnoreTag(str);
|
||||
title = getAndRemoveDocisfyIgnoreConfig(str).content;
|
||||
}
|
||||
|
||||
index[slug] = { slug, title: title, body: '' };
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
removeAtag,
|
||||
getAndRemoveConfig,
|
||||
getAndRemoveDocisfyIgnorConfig,
|
||||
getAndRemoveDocisfyIgnoreConfig,
|
||||
} from '../../src/core/render/utils.js';
|
||||
import { tree } from '../../src/core/render/tpl.js';
|
||||
import { slugify } from '../../src/core/render/slugify.js';
|
||||
@ -24,7 +24,7 @@ describe('core/render/utils', () => {
|
||||
describe('getAndRemoveDocisfyIgnorConfig()', () => {
|
||||
test('getAndRemoveDocisfyIgnorConfig from <!-- {docsify-ignore} -->', () => {
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnorConfig(
|
||||
getAndRemoveDocisfyIgnoreConfig(
|
||||
'My Ignore Title<!-- {docsify-ignore} -->'
|
||||
);
|
||||
expect(content).toBe('My Ignore Title');
|
||||
@ -34,7 +34,7 @@ describe('core/render/utils', () => {
|
||||
|
||||
test('getAndRemoveDocisfyIgnorConfig from <!-- {docsify-ignore-all} -->', () => {
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnorConfig(
|
||||
getAndRemoveDocisfyIgnoreConfig(
|
||||
'My Ignore Title<!-- {docsify-ignore-all} -->'
|
||||
);
|
||||
expect(content).toBe('My Ignore Title');
|
||||
@ -44,7 +44,7 @@ describe('core/render/utils', () => {
|
||||
|
||||
test('getAndRemoveDocisfyIgnorConfig from {docsify-ignore}', () => {
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnorConfig('My Ignore Title{docsify-ignore}');
|
||||
getAndRemoveDocisfyIgnoreConfig('My Ignore Title{docsify-ignore}');
|
||||
expect(content).toBe('My Ignore Title');
|
||||
expect(ignoreSubHeading).toBeTruthy();
|
||||
expect(ignoreAllSubs === undefined).toBeTruthy();
|
||||
@ -52,7 +52,7 @@ describe('core/render/utils', () => {
|
||||
|
||||
test('getAndRemoveDocisfyIgnorConfig from {docsify-ignore-all}', () => {
|
||||
const { content, ignoreAllSubs, ignoreSubHeading } =
|
||||
getAndRemoveDocisfyIgnorConfig('My Ignore Title{docsify-ignore-all}');
|
||||
getAndRemoveDocisfyIgnoreConfig('My Ignore Title{docsify-ignore-all}');
|
||||
expect(content).toBe('My Ignore Title');
|
||||
expect(ignoreAllSubs).toBeTruthy();
|
||||
expect(ignoreSubHeading === undefined).toBeTruthy();
|
||||
|
Loading…
Reference in New Issue
Block a user