chore: added new linter config (#1028)

* chore: added new linter config

* chore: linting fixes

* chore: refactore in linting config and minor linting fixes
This commit is contained in:
Anix 2020-02-20 14:01:05 +05:30 committed by GitHub
parent a111df9980
commit 75c72e917b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 2556 additions and 1764 deletions

View File

@ -5,3 +5,7 @@ build
server.js server.js
cypress cypress
lib lib
themes
build
docs/
**/*.md

View File

@ -1,34 +0,0 @@
{
"extends": "xo-space/browser",
"rules": {
"semi": [
2,
"never"
],
"no-return-assign": "off",
"no-unused-expressions": "off",
"no-new-func": "off",
"no-multi-assign": "off",
"no-mixed-operators": "off",
"max-params": "off",
"no-script-url": "off",
"camelcase": "off",
"object-curly-spacing": "off",
"no-warning-comments": "off",
"no-negated-condition": "off",
"eqeqeq": "warn",
"no-eq-null": "warn",
"max-statements-per-line": "warn"
},
"globals": {
"Docsify": true,
"$docsify": true,
"process": true
},
"env": {
"browser": true,
"amd": true,
"node": true,
"jest": true
}
}

63
.eslintrc.js Normal file
View File

@ -0,0 +1,63 @@
module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module',
ecmaVersion: 2019,
},
env: {
jest: true,
browser: true,
node: true,
es6: true,
},
plugins: ['prettier', 'import'],
extends: ['eslint:recommended', 'plugin:import/recommended'],
settings: {
'import/ignore': ['node_modules', '.json$'],
},
rules: {
'prettier/prettier': ['error'],
camelcase: ['warn'],
curly: ['error', 'all'],
'dot-notation': ['error'],
eqeqeq: ['error'],
'handle-callback-err': ['error'],
'new-cap': ['error'],
'no-alert': ['error'],
'no-caller': ['error'],
'no-eval': ['error'],
'no-labels': ['error'],
'no-lonely-if': ['error'],
'no-new': ['error'],
'no-proto': ['error'],
'no-return-assign': ['error'],
'no-self-compare': ['error'],
'no-shadow': ['warn'],
'no-shadow-restricted-names': ['error'],
'no-useless-call': ['error'],
'no-var': ['error'],
'no-void': ['error'],
'no-with': ['error'],
radix: ['error'],
'spaced-comment': ['error', 'always'],
strict: ['error', 'global'],
yoda: ['error', 'never'],
// Import rules
// Search way how integrate with `lerna`
'import/no-unresolved': 'off',
'import/imports-first': ['error'],
'import/newline-after-import': ['error'],
'import/no-duplicates': ['error'],
'import/no-mutable-exports': ['error'],
'import/no-named-as-default': ['error'],
'import/no-named-as-default-member': ['error'],
'import/order': ['warn'],
},
globals: {
Docsify: 'writable',
$docsify: 'writable',
dom: 'writable',
},
};

4
.prettierrc.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
singleQuote: true,
trailingComma: 'es5',
};

580
package-lock.json generated
View File

@ -13,6 +13,61 @@
"@babel/highlight": "^7.0.0" "@babel/highlight": "^7.0.0"
} }
}, },
"@babel/generator": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
"integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
"dev": true,
"requires": {
"@babel/types": "^7.8.3",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
},
"dependencies": {
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
}
}
},
"@babel/helper-function-name": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz",
"integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==",
"dev": true,
"requires": {
"@babel/helper-get-function-arity": "^7.8.3",
"@babel/template": "^7.8.3",
"@babel/types": "^7.8.3"
}
},
"@babel/helper-get-function-arity": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
"integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
"dev": true,
"requires": {
"@babel/types": "^7.8.3"
}
},
"@babel/helper-split-export-declaration": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
"integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
"dev": true,
"requires": {
"@babel/types": "^7.8.3"
}
},
"@babel/highlight": { "@babel/highlight": {
"version": "7.5.0", "version": "7.5.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
@ -24,6 +79,95 @@
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
}, },
"@babel/parser": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
"integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
"dev": true
},
"@babel/template": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
"integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@babel/parser": "^7.8.3",
"@babel/types": "^7.8.3"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
"dev": true,
"requires": {
"@babel/highlight": "^7.8.3"
}
},
"@babel/highlight": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
"integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
"dev": true,
"requires": {
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
}
}
}
},
"@babel/traverse": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
"integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
"@babel/generator": "^7.8.4",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
"@babel/parser": "^7.8.4",
"@babel/types": "^7.8.3",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
"integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
"dev": true,
"requires": {
"@babel/highlight": "^7.8.3"
}
},
"@babel/highlight": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
"integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
"dev": true,
"requires": {
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
}
}
}
},
"@babel/types": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
"integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.13",
"to-fast-properties": "^2.0.0"
}
},
"@cypress/listr-verbose-renderer": { "@cypress/listr-verbose-renderer": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz", "resolved": "https://registry.npmjs.org/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz",
@ -1888,6 +2032,96 @@
"integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=",
"dev": true "dev": true
}, },
"array-includes": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
"integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0",
"is-string": "^1.0.5"
},
"dependencies": {
"es-abstract": {
"version": "1.17.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
"integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.1.5",
"is-regex": "^1.0.5",
"object-inspect": "^1.7.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimleft": "^2.1.1",
"string.prototype.trimright": "^2.1.1"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true
},
"is-callable": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
"integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
"dev": true
},
"is-regex": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
"integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"object-inspect": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"dev": true
},
"string.prototype.trimleft": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
"integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
}
},
"string.prototype.trimright": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
"integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
}
}
}
},
"array-union": { "array-union": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@ -1909,6 +2143,95 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true "dev": true
}, },
"array.prototype.flat": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
"integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.17.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
"integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.1.5",
"is-regex": "^1.0.5",
"object-inspect": "^1.7.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimleft": "^2.1.1",
"string.prototype.trimright": "^2.1.1"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true
},
"is-callable": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
"integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
"dev": true
},
"is-regex": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
"integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"object-inspect": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"dev": true
},
"string.prototype.trimleft": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
"integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
}
},
"string.prototype.trimright": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
"integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
}
}
}
},
"arrify": { "arrify": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@ -2042,6 +2365,20 @@
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
"dev": true "dev": true
}, },
"babel-eslint": {
"version": "10.0.3",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz",
"integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/parser": "^7.0.0",
"@babel/traverse": "^7.0.0",
"@babel/types": "^7.0.0",
"eslint-visitor-keys": "^1.0.0",
"resolve": "^1.12.0"
}
},
"babylon": { "babylon": {
"version": "6.18.0", "version": "6.18.0",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
@ -2860,6 +3197,12 @@
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true "dev": true
}, },
"contains-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
"integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
"dev": true
},
"conventional-changelog": { "conventional-changelog": {
"version": "3.1.12", "version": "3.1.12",
"resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.12.tgz", "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.12.tgz",
@ -4597,19 +4940,189 @@
} }
} }
}, },
"eslint-config-xo": { "eslint-import-resolver-node": {
"version": "0.26.0", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/eslint-config-xo/-/eslint-config-xo-0.26.0.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz",
"integrity": "sha512-l+93kmBSNr5rMrsqwC6xVWsi8LI4He3z6jSk38e9bAkMNsVsQ8XYO+qzXfJFgFX4i/+hiTswyHtl+nDut9rPaA==", "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==",
"dev": true
},
"eslint-config-xo-space": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/eslint-config-xo-space/-/eslint-config-xo-space-0.21.0.tgz",
"integrity": "sha512-4/Ko662Ghi1hgAF5H9Z37isg7WAeW1eGaY85pf/KLdLaif5qSPqpRZzaFzKjfCgkz3tU1GgpUIYYcMpLetNdyQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"eslint-config-xo": "^0.26.0" "debug": "^2.6.9",
"resolve": "^1.13.1"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"resolve": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
"integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
"dev": true,
"requires": {
"path-parse": "^1.0.6"
}
}
}
},
"eslint-module-utils": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz",
"integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"pkg-dir": "^2.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
"dev": true,
"requires": {
"find-up": "^2.1.0"
}
}
}
},
"eslint-plugin-import": {
"version": "2.20.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz",
"integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
"array.prototype.flat": "^1.2.1",
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.4.1",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.0",
"read-pkg-up": "^2.0.0",
"resolve": "^1.12.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"isarray": "^1.0.0"
}
},
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"strip-bom": "^3.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
}
},
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
"dev": true,
"requires": {
"pify": "^2.0.0"
}
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
"dev": true,
"requires": {
"load-json-file": "^2.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^2.0.0"
}
},
"read-pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
"dev": true,
"requires": {
"find-up": "^2.0.0",
"read-pkg": "^2.0.0"
}
}
}
},
"eslint-plugin-prettier": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz",
"integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==",
"dev": true,
"requires": {
"prettier-linter-helpers": "^1.0.0"
} }
}, },
"eslint-scope": { "eslint-scope": {
@ -5026,6 +5539,12 @@
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true "dev": true
}, },
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"fast-glob": { "fast-glob": {
"version": "2.2.7", "version": "2.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
@ -6826,6 +7345,12 @@
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true "dev": true
}, },
"is-string": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
"dev": true
},
"is-svg": { "is-svg": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
@ -9389,6 +9914,18 @@
"isobject": "^3.0.0" "isobject": "^3.0.0"
} }
}, },
"object.assign": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.0",
"object-keys": "^1.0.11"
}
},
"object.getownpropertydescriptors": { "object.getownpropertydescriptors": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
@ -10655,6 +11192,21 @@
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"dev": true "dev": true
}, },
"prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"requires": {
"fast-diff": "^1.1.2"
}
},
"prismjs": { "prismjs": {
"version": "1.17.1", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz",
@ -12626,6 +13178,12 @@
"os-tmpdir": "~1.0.2" "os-tmpdir": "~1.0.2"
} }
}, },
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
},
"to-object-path": { "to-object-path": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",

View File

@ -70,6 +70,7 @@
}, },
"devDependencies": { "devDependencies": {
"autoprefixer-stylus": "^1.0.0", "autoprefixer-stylus": "^1.0.0",
"babel-eslint": "^10.0.3",
"chai": "^4.2.0", "chai": "^4.2.0",
"chokidar": "^3.2.1", "chokidar": "^3.2.1",
"conventional-changelog-cli": "^2.0.25", "conventional-changelog-cli": "^2.0.25",
@ -80,7 +81,8 @@
"cypress": "^3.8.1", "cypress": "^3.8.1",
"cypress-image-snapshot": "^3.1.1", "cypress-image-snapshot": "^3.1.1",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-xo-space": "^0.21.0", "eslint-plugin-import": "^2.20.1",
"eslint-plugin-prettier": "^3.1.2",
"esm": "^3.1.4", "esm": "^3.1.4",
"husky": "^3.1.0", "husky": "^3.1.0",
"jsdom": "^15.1.1", "jsdom": "^15.1.1",
@ -90,6 +92,7 @@
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"prettier": "^1.19.1",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"rollup": "^1.23.1", "rollup": "^1.23.1",
"rollup-plugin-async": "^1.2.0", "rollup-plugin-async": "^1.2.0",

View File

@ -1,116 +1,116 @@
import * as tpl from '../../src/core/render/tpl' import { readFileSync } from 'fs';
import fetch from 'node-fetch' import { resolve, basename } from 'path';
import {AbstractHistory} from '../../src/core/router/history/abstract' import { AbstractHistory } from '../../src/core/router/history/abstract';
import {Compiler} from '../../src/core/render/compiler' import { Compiler } from '../../src/core/render/compiler';
import {isAbsolutePath} from '../../src/core/router/util' import { isAbsolutePath } from '../../src/core/router/util';
import {readFileSync} from 'fs' import * as tpl from '../../src/core/render/tpl';
import {resolve, basename} from 'path' import { prerenderEmbed } from '../../src/core/render/embed';
import resolvePathname from 'resolve-pathname' import resolvePathname from 'resolve-pathname';
import debug from 'debug' import debug from 'debug';
import {prerenderEmbed} from '../../src/core/render/embed' import fetch from 'node-fetch';
function cwd(...args) { function cwd(...args) {
return resolve(process.cwd(), ...args) return resolve(process.cwd(), ...args);
} }
function mainTpl(config) { function mainTpl(config) {
let html = `<nav class="app-nav${ let html = `<nav class="app-nav${
config.repo ? '' : ' no-badge' config.repo ? '' : ' no-badge'
}"><!--navbar--></nav>` }"><!--navbar--></nav>`;
if (config.repo) { if (config.repo) {
html += tpl.corner(config.repo) html += tpl.corner(config.repo);
} }
if (config.coverpage) { if (config.coverpage) {
html += tpl.cover() html += tpl.cover();
} }
html += tpl.main(config) html += tpl.main(config);
return html return html;
} }
export default class Renderer { export default class Renderer {
constructor({ template, config, cache }) { constructor({ template, config, cache }) {
this.html = template this.html = template;
this.config = config = Object.assign({}, config, { this.config = config = Object.assign({}, config, {
routerMode: 'history' routerMode: 'history',
}) });
this.cache = cache this.cache = cache;
this.router = new AbstractHistory(config) this.router = new AbstractHistory(config);
this.compiler = new Compiler(config, this.router) this.compiler = new Compiler(config, this.router);
this.router.getCurrentPath = () => this.url this.router.getCurrentPath = () => this.url;
this._renderHtml( this._renderHtml(
'inject-config', 'inject-config',
`<script>window.$docsify = ${JSON.stringify(config)}</script>` `<script>window.$docsify = ${JSON.stringify(config)}</script>`
) );
this._renderHtml('inject-app', mainTpl(config)) this._renderHtml('inject-app', mainTpl(config));
this.template = this.html this.template = this.html;
} }
_getPath(url) { _getPath(url) {
const file = this.router.getFile(url) const file = this.router.getFile(url);
return isAbsolutePath(file) ? file : cwd(`./${file}`) return isAbsolutePath(file) ? file : cwd(`./${file}`);
} }
async renderToString(url) { async renderToString(url) {
this.url = url = this.router.parse(url).path this.url = url = this.router.parse(url).path;
const {loadSidebar, loadNavbar, coverpage} = this.config const { loadSidebar, loadNavbar, coverpage } = this.config;
const mainFile = this._getPath(url) const mainFile = this._getPath(url);
this._renderHtml('main', await this._render(mainFile, 'main')) this._renderHtml('main', await this._render(mainFile, 'main'));
if (loadSidebar) { if (loadSidebar) {
const name = loadSidebar === true ? '_sidebar.md' : loadSidebar const name = loadSidebar === true ? '_sidebar.md' : loadSidebar;
const sidebarFile = this._getPath(resolve(url, `./${name}`)) const sidebarFile = this._getPath(resolve(url, `./${name}`));
this._renderHtml('sidebar', await this._render(sidebarFile, 'sidebar')) this._renderHtml('sidebar', await this._render(sidebarFile, 'sidebar'));
} }
if (loadNavbar) { if (loadNavbar) {
const name = loadNavbar === true ? '_navbar.md' : loadNavbar const name = loadNavbar === true ? '_navbar.md' : loadNavbar;
const navbarFile = this._getPath(resolve(url, `./${name}`)) const navbarFile = this._getPath(resolve(url, `./${name}`));
this._renderHtml('navbar', await this._render(navbarFile, 'navbar')) this._renderHtml('navbar', await this._render(navbarFile, 'navbar'));
} }
if (coverpage) { if (coverpage) {
let path = null let path = null;
if (typeof coverpage === 'string') { if (typeof coverpage === 'string') {
if (url === '/') { if (url === '/') {
path = coverpage path = coverpage;
} }
} else if (Array.isArray(coverpage)) { } else if (Array.isArray(coverpage)) {
path = coverpage.indexOf(url) > -1 && '_coverpage.md' path = coverpage.indexOf(url) > -1 && '_coverpage.md';
} else { } else {
const cover = coverpage[url] const cover = coverpage[url];
path = cover === true ? '_coverpage.md' : cover path = cover === true ? '_coverpage.md' : cover;
} }
const coverFile = this._getPath(resolve(url, `./${path}`)) const coverFile = this._getPath(resolve(url, `./${path}`));
this._renderHtml('cover', await this._render(coverFile), 'cover') this._renderHtml('cover', await this._render(coverFile), 'cover');
} }
const html = this.html const html = this.html;
this.html = this.template this.html = this.template;
return html return html;
} }
_renderHtml(match, content) { _renderHtml(match, content) {
this.html = this.html.replace(new RegExp(`<!--${match}-->`, 'g'), content) this.html = this.html.replace(new RegExp(`<!--${match}-->`, 'g'), content);
return this.html return this.html;
} }
async _render(path, type) { async _render(path, type) {
let html = await this._loadFile(path) let html = await this._loadFile(path);
const {subMaxLevel, maxLevel} = this.config const { subMaxLevel, maxLevel } = this.config;
let tokens let tokens;
switch (type) { switch (type) {
case 'sidebar': case 'sidebar':
@ -118,67 +118,67 @@ export default class Renderer {
this.compiler.sidebar(html, maxLevel) + this.compiler.sidebar(html, maxLevel) +
`<script>window.__SUB_SIDEBAR__ = ${JSON.stringify( `<script>window.__SUB_SIDEBAR__ = ${JSON.stringify(
this.compiler.subSidebar(subMaxLevel) this.compiler.subSidebar(subMaxLevel)
)}</script>` )}</script>`;
break break;
case 'cover': case 'cover':
html = this.compiler.cover(html) html = this.compiler.cover(html);
break break;
case 'main': case 'main':
tokens = await new Promise(r => { tokens = await new Promise(r => {
prerenderEmbed( prerenderEmbed(
{ {
fetch: url => this._loadFile(this._getPath(url)), fetch: url => this._loadFile(this._getPath(url)),
compiler: this.compiler, compiler: this.compiler,
raw: html raw: html,
}, },
r r
) );
}) });
html = this.compiler.compile(tokens) html = this.compiler.compile(tokens);
break break;
case 'navbar': case 'navbar':
case 'article': case 'article':
default: default:
html = this.compiler.compile(html) html = this.compiler.compile(html);
break break;
} }
return html return html;
} }
async _loadFile(filePath) { async _loadFile(filePath) {
debug('docsify')(`load > ${filePath}`) debug('docsify')(`load > ${filePath}`);
let content let content;
try { try {
if (isAbsolutePath(filePath)) { if (isAbsolutePath(filePath)) {
const res = await fetch(filePath) const res = await fetch(filePath);
if (!res.ok) { if (!res.ok) {
throw Error() throw Error();
} }
content = await res.text() content = await res.text();
this.lock = 0 this.lock = 0;
} else { } else {
content = await readFileSync(filePath, 'utf8') content = await readFileSync(filePath, 'utf8');
this.lock = 0 this.lock = 0;
} }
return content return content;
} catch (e) { } catch (e) {
this.lock = this.lock || 0 this.lock = this.lock || 0;
if (++this.lock > 10) { if (++this.lock > 10) {
this.lock = 0 this.lock = 0;
return return;
} }
const fileName = basename(filePath) const fileName = basename(filePath);
const result = await this._loadFile( const result = await this._loadFile(
resolvePathname(`../${fileName}`, filePath) resolvePathname(`../${fileName}`, filePath)
) );
return result return result;
} }
} }
} }
Renderer.version = '__VERSION__' Renderer.version = '__VERSION__';

View File

@ -1,6 +1,6 @@
import {merge, hyphenate, isPrimitive, hasOwn} from './util/core' import { merge, hyphenate, isPrimitive, hasOwn } from './util/core';
const currentScript = document.currentScript const currentScript = document.currentScript;
export default function() { export default function() {
const config = merge( const config = merge(
@ -32,50 +32,50 @@ export default function () {
externalLinkRel: 'noopener', externalLinkRel: 'noopener',
routerMode: 'hash', routerMode: 'hash',
noCompileLinks: [], noCompileLinks: [],
relativePath: false relativePath: false,
}, },
window.$docsify window.$docsify
) );
const script = const script =
currentScript || currentScript ||
[].slice [].slice
.call(document.getElementsByTagName('script')) .call(document.getElementsByTagName('script'))
.filter(n => /docsify\./.test(n.src))[0] .filter(n => /docsify\./.test(n.src))[0];
if (script) { if (script) {
for (const prop in config) { for (const prop in config) {
if (hasOwn.call(config, prop)) { if (hasOwn.call(config, prop)) {
const val = script.getAttribute('data-' + hyphenate(prop)) const val = script.getAttribute('data-' + hyphenate(prop));
if (isPrimitive(val)) { if (isPrimitive(val)) {
config[prop] = val === '' ? true : val config[prop] = val === '' ? true : val;
} }
} }
} }
} }
if (config.loadSidebar === true) { if (config.loadSidebar === true) {
config.loadSidebar = '_sidebar' + config.ext config.loadSidebar = '_sidebar' + config.ext;
} }
if (config.loadNavbar === true) { if (config.loadNavbar === true) {
config.loadNavbar = '_navbar' + config.ext config.loadNavbar = '_navbar' + config.ext;
} }
if (config.coverpage === true) { if (config.coverpage === true) {
config.coverpage = '_coverpage' + config.ext config.coverpage = '_coverpage' + config.ext;
} }
if (config.repo === true) { if (config.repo === true) {
config.repo = '' config.repo = '';
} }
if (config.name === true) { if (config.name === true) {
config.name = '' config.name = '';
} }
window.$docsify = config window.$docsify = config;
return config return config;
} }

View File

@ -1,26 +1,26 @@
import {isMobile} from '../util/env' import { isMobile } from '../util/env';
import {body, on} from '../util/dom' import { body, on } from '../util/dom';
import * as sidebar from './sidebar' import * as sidebar from './sidebar';
import {scrollIntoView} from './scroll' import { scrollIntoView } from './scroll';
export function eventMixin(proto) { export function eventMixin(proto) {
proto.$resetEvents = function() { proto.$resetEvents = function() {
scrollIntoView(this.route.path, this.route.query.id) scrollIntoView(this.route.path, this.route.query.id);
if (this.config.loadNavbar) { if (this.config.loadNavbar) {
sidebar.getAndActive(this.router, 'nav') sidebar.getAndActive(this.router, 'nav');
}
} }
};
} }
export function initEvent(vm) { export function initEvent(vm) {
// Bind toggle button // Bind toggle button
sidebar.btn('button.sidebar-toggle', vm.router) sidebar.btn('button.sidebar-toggle', vm.router);
sidebar.collapse('.sidebar', vm.router) sidebar.collapse('.sidebar', vm.router);
// Bind sticky effect // Bind sticky effect
if (vm.config.coverpage) { if (vm.config.coverpage) {
!isMobile && on('scroll', sidebar.sticky) !isMobile && on('scroll', sidebar.sticky);
} else { } else {
body.classList.add('sticky') body.classList.add('sticky');
} }
} }

View File

@ -1,156 +1,160 @@
import { isMobile } from '../util/env' import { isMobile } from '../util/env';
import * as dom from '../util/dom' import * as dom from '../util/dom';
import Tweezer from 'tweezer.js' import Tweezer from 'tweezer.js';
import cssEscape from 'css.escape' import cssEscape from 'css.escape';
const nav = {} const nav = {};
let hoverOver = false let hoverOver = false;
let scroller = null let scroller = null;
let enableScrollEvent = true let enableScrollEvent = true;
let coverHeight = 0 let coverHeight = 0;
function scrollTo(el) { function scrollTo(el) {
if (scroller) { if (scroller) {
scroller.stop() scroller.stop();
} }
enableScrollEvent = false enableScrollEvent = false;
scroller = new Tweezer({ scroller = new Tweezer({
start: window.pageYOffset, start: window.pageYOffset,
end: el.getBoundingClientRect().top + window.pageYOffset, end: el.getBoundingClientRect().top + window.pageYOffset,
duration: 500 duration: 500,
}) })
.on('tick', v => window.scrollTo(0, v)) .on('tick', v => window.scrollTo(0, v))
.on('done', () => { .on('done', () => {
enableScrollEvent = true enableScrollEvent = true;
scroller = null scroller = null;
}) })
.begin() .begin();
} }
function highlight(path) { function highlight(path) {
if (!enableScrollEvent) { if (!enableScrollEvent) {
return return;
} }
const sidebar = dom.getNode('.sidebar') const sidebar = dom.getNode('.sidebar');
const anchors = dom.findAll('.anchor') const anchors = dom.findAll('.anchor');
const wrap = dom.find(sidebar, '.sidebar-nav') const wrap = dom.find(sidebar, '.sidebar-nav');
let active = dom.find(sidebar, 'li.active') let active = dom.find(sidebar, 'li.active');
const doc = document.documentElement const doc = document.documentElement;
const top = ((doc && doc.scrollTop) || document.body.scrollTop) - coverHeight const top = ((doc && doc.scrollTop) || document.body.scrollTop) - coverHeight;
let last let last;
for (let i = 0, len = anchors.length; i < len; i += 1) { for (let i = 0, len = anchors.length; i < len; i += 1) {
const node = anchors[i] const node = anchors[i];
if (node.offsetTop > top) { if (node.offsetTop > top) {
if (!last) { if (!last) {
last = node last = node;
} }
break break;
} else { } else {
last = node last = node;
} }
} }
if (!last) { if (!last) {
return return;
} }
const li = nav[getNavKey(decodeURIComponent(path), last.getAttribute('data-id'))] const li =
nav[getNavKey(decodeURIComponent(path), last.getAttribute('data-id'))];
if (!li || li === active) { if (!li || li === active) {
return return;
} }
active && active.classList.remove('active') active && active.classList.remove('active');
li.classList.add('active') li.classList.add('active');
active = li active = li;
// Scroll into view // Scroll into view
// https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297 // https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297
if (!hoverOver && dom.body.classList.contains('sticky')) { if (!hoverOver && dom.body.classList.contains('sticky')) {
const height = sidebar.clientHeight const height = sidebar.clientHeight;
const curOffset = 0 const curOffset = 0;
const cur = active.offsetTop + active.clientHeight + 40 const cur = active.offsetTop + active.clientHeight + 40;
const isInView = const isInView =
active.offsetTop >= wrap.scrollTop && cur <= wrap.scrollTop + height active.offsetTop >= wrap.scrollTop && cur <= wrap.scrollTop + height;
const notThan = cur - curOffset < height const notThan = cur - curOffset < height;
const top = isInView ? wrap.scrollTop : notThan ? curOffset : cur - height const top = isInView ? wrap.scrollTop : notThan ? curOffset : cur - height;
sidebar.scrollTop = top sidebar.scrollTop = top;
} }
} }
function getNavKey(path, id) { function getNavKey(path, id) {
return `${path}?id=${id}` return `${path}?id=${id}`;
} }
export function scrollActiveSidebar(router) { export function scrollActiveSidebar(router) {
const cover = dom.find('.cover.show') const cover = dom.find('.cover.show');
coverHeight = cover ? cover.offsetHeight : 0 coverHeight = cover ? cover.offsetHeight : 0;
const sidebar = dom.getNode('.sidebar') const sidebar = dom.getNode('.sidebar');
let lis = [] let lis = [];
if (sidebar !== null && sidebar !== undefined) { if (sidebar !== null && sidebar !== undefined) {
lis = dom.findAll(sidebar, 'li') lis = dom.findAll(sidebar, 'li');
} }
for (let i = 0, len = lis.length; i < len; i += 1) { for (let i = 0, len = lis.length; i < len; i += 1) {
const li = lis[i] const li = lis[i];
const a = li.querySelector('a') const a = li.querySelector('a');
if (!a) { if (!a) {
continue continue;
} }
let href = a.getAttribute('href') let href = a.getAttribute('href');
if (href !== '/') { if (href !== '/') {
const { query: { id }, path } = router.parse(href) const {
query: { id },
path,
} = router.parse(href);
if (id) { if (id) {
href = getNavKey(path, id) href = getNavKey(path, id);
} }
} }
if (href) { if (href) {
nav[decodeURIComponent(href)] = li nav[decodeURIComponent(href)] = li;
} }
} }
if (isMobile) { if (isMobile) {
return return;
} }
const path = router.getCurrentPath() const path = router.getCurrentPath();
dom.off('scroll', () => highlight(path)) dom.off('scroll', () => highlight(path));
dom.on('scroll', () => highlight(path)) dom.on('scroll', () => highlight(path));
dom.on(sidebar, 'mouseover', () => { dom.on(sidebar, 'mouseover', () => {
hoverOver = true hoverOver = true;
}) });
dom.on(sidebar, 'mouseleave', () => { dom.on(sidebar, 'mouseleave', () => {
hoverOver = false hoverOver = false;
}) });
} }
export function scrollIntoView(path, id) { export function scrollIntoView(path, id) {
if (!id) { if (!id) {
return return;
} }
const section = dom.find('#' + cssEscape(id)) const section = dom.find('#' + cssEscape(id));
section && scrollTo(section) section && scrollTo(section);
const li = nav[getNavKey(path, id)] const li = nav[getNavKey(path, id)];
const sidebar = dom.getNode('.sidebar') const sidebar = dom.getNode('.sidebar');
const active = dom.find(sidebar, 'li.active') const active = dom.find(sidebar, 'li.active');
active && active.classList.remove('active') active && active.classList.remove('active');
li && li.classList.add('active') li && li.classList.add('active');
} }
const scrollEl = dom.$.scrollingElement || dom.$.documentElement const scrollEl = dom.$.scrollingElement || dom.$.documentElement;
export function scroll2Top(offset = 0) { export function scroll2Top(offset = 0) {
scrollEl.scrollTop = offset === true ? 0 : Number(offset) scrollEl.scrollTop = offset === true ? 0 : Number(offset);
} }

View File

@ -1,37 +1,38 @@
import { isMobile } from '../util/env' /* eslint-disable no-unused-vars */
import * as dom from '../util/dom' import { isMobile } from '../util/env';
import * as dom from '../util/dom';
const title = dom.$.title const title = dom.$.title;
/** /**
* Toggle button * Toggle button
* @param {Element} el Button to be toggled * @param {Element} el Button to be toggled
* @void * @void
*/ */
export function btn(el) { export function btn(el) {
const toggle = _ => dom.body.classList.toggle('close') const toggle = _ => dom.body.classList.toggle('close');
el = dom.getNode(el) el = dom.getNode(el);
if (el === null || el === undefined) { if (el === null || el === undefined) {
return return;
} }
dom.on(el, 'click', e => { dom.on(el, 'click', e => {
e.stopPropagation() e.stopPropagation();
toggle() toggle();
}) });
isMobile && isMobile &&
dom.on( dom.on(
dom.body, dom.body,
'click', 'click',
_ => dom.body.classList.contains('close') && toggle() _ => dom.body.classList.contains('close') && toggle()
) );
} }
export function collapse(el) { export function collapse(el) {
el = dom.getNode(el) el = dom.getNode(el);
if (el === null || el === undefined) { if (el === null || el === undefined) {
return return;
} }
dom.on(el, 'click', ({ target }) => { dom.on(el, 'click', ({ target }) => {
@ -40,23 +41,23 @@ export function collapse(el) {
target.nextSibling && target.nextSibling &&
target.nextSibling.classList.contains('app-sub-sidebar') target.nextSibling.classList.contains('app-sub-sidebar')
) { ) {
dom.toggleClass(target.parentNode, 'collapse') dom.toggleClass(target.parentNode, 'collapse');
} }
}) });
} }
export function sticky() { export function sticky() {
const cover = dom.getNode('section.cover') const cover = dom.getNode('section.cover');
if (!cover) { if (!cover) {
return return;
} }
const coverHeight = cover.getBoundingClientRect().height const coverHeight = cover.getBoundingClientRect().height;
if (window.pageYOffset >= coverHeight || cover.classList.contains('hidden')) { if (window.pageYOffset >= coverHeight || cover.classList.contains('hidden')) {
dom.toggleClass(dom.body, 'add', 'sticky') dom.toggleClass(dom.body, 'add', 'sticky');
} else { } else {
dom.toggleClass(dom.body, 'remove', 'sticky') dom.toggleClass(dom.body, 'remove', 'sticky');
} }
} }
@ -69,30 +70,34 @@ export function sticky() {
* @return {Element} Active element * @return {Element} Active element
*/ */
export function getAndActive(router, el, isParent, autoTitle) { export function getAndActive(router, el, isParent, autoTitle) {
el = dom.getNode(el) el = dom.getNode(el);
let links = [] let links = [];
if (el !== null && el !== undefined) { if (el !== null && el !== undefined) {
links = dom.findAll(el, 'a') links = dom.findAll(el, 'a');
} }
const hash = decodeURI(router.toURL(router.getCurrentPath())) const hash = decodeURI(router.toURL(router.getCurrentPath()));
let target let target;
links.sort((a, b) => b.href.length - a.href.length).forEach(a => { links
const href = a.getAttribute('href') .sort((a, b) => b.href.length - a.href.length)
const node = isParent ? a.parentNode : a .forEach(a => {
const href = a.getAttribute('href');
const node = isParent ? a.parentNode : a;
if (hash.indexOf(href) === 0 && !target) { if (hash.indexOf(href) === 0 && !target) {
target = a target = a;
dom.toggleClass(node, 'add', 'active') dom.toggleClass(node, 'add', 'active');
} else { } else {
dom.toggleClass(node, 'remove', 'active') dom.toggleClass(node, 'remove', 'active');
} }
}) });
if (autoTitle) { if (autoTitle) {
dom.$.title = target ? (target.title || `${target.innerText} - ${title}`) : title dom.$.title = target
? target.title || `${target.innerText} - ${title}`
: title;
} }
return target return target;
} }

View File

@ -1,7 +1,8 @@
import progressbar from '../render/progressbar' /* eslint-disable no-unused-vars */
import { noop, hasOwn } from '../util/core' import progressbar from '../render/progressbar';
import { noop, hasOwn } from '../util/core';
const cache = {} const cache = {};
/** /**
* Ajax GET implmentation * Ajax GET implmentation
@ -11,25 +12,25 @@ const cache = {}
* @return {Promise} Promise response * @return {Promise} Promise response
*/ */
export function get(url, hasBar = false, headers = {}) { export function get(url, hasBar = false, headers = {}) {
const xhr = new XMLHttpRequest() const xhr = new XMLHttpRequest();
const on = function() { const on = function() {
xhr.addEventListener.apply(xhr, arguments) xhr.addEventListener.apply(xhr, arguments);
} };
const cached = cache[url] const cached = cache[url];
if (cached) { if (cached) {
return { then: cb => cb(cached.content, cached.opt), abort: noop } return { then: cb => cb(cached.content, cached.opt), abort: noop };
} }
xhr.open('GET', url) xhr.open('GET', url);
for (const i in headers) { for (const i in headers) {
if (hasOwn.call(headers, i)) { if (hasOwn.call(headers, i)) {
xhr.setRequestHeader(i, headers[i]) xhr.setRequestHeader(i, headers[i]);
} }
} }
xhr.send() xhr.send();
return { return {
then: function(success, error = noop) { then: function(success, error = noop) {
@ -37,34 +38,34 @@ export function get(url, hasBar = false, headers = {}) {
const id = setInterval( const id = setInterval(
_ => _ =>
progressbar({ progressbar({
step: Math.floor(Math.random() * 5 + 1) step: Math.floor(Math.random() * 5 + 1),
}), }),
500 500
) );
on('progress', progressbar) on('progress', progressbar);
on('loadend', evt => { on('loadend', evt => {
progressbar(evt) progressbar(evt);
clearInterval(id) clearInterval(id);
}) });
} }
on('error', error) on('error', error);
on('load', ({ target }) => { on('load', ({ target }) => {
if (target.status >= 400) { if (target.status >= 400) {
error(target) error(target);
} else { } else {
const result = (cache[url] = { const result = (cache[url] = {
content: target.response, content: target.response,
opt: { opt: {
updatedAt: xhr.getResponseHeader('last-modified') updatedAt: xhr.getResponseHeader('last-modified'),
}
})
success(result.content, result.opt)
}
})
}, },
abort: _ => xhr.readyState !== 4 && xhr.abort() });
success(result.content, result.opt);
} }
});
},
abort: _ => xhr.readyState !== 4 && xhr.abort(),
};
} }

View File

@ -1,90 +1,91 @@
import { get } from './ajax' /* eslint-disable no-unused-vars */
import { callHook } from '../init/lifecycle' import { callHook } from '../init/lifecycle';
import { getParentPath, stringifyQuery } from '../router/util' import { getParentPath, stringifyQuery } from '../router/util';
import { noop } from '../util/core' import { noop } from '../util/core';
import { getAndActive } from '../event/sidebar' import { getAndActive } from '../event/sidebar';
import { get } from './ajax';
function loadNested(path, qs, file, next, vm, first) { function loadNested(path, qs, file, next, vm, first) {
path = first ? path : path.replace(/\/$/, '') path = first ? path : path.replace(/\/$/, '');
path = getParentPath(path) path = getParentPath(path);
if (!path) { if (!path) {
return return;
} }
get( get(
vm.router.getFile(path + file) + qs, vm.router.getFile(path + file) + qs,
false, false,
vm.config.requestHeaders vm.config.requestHeaders
).then(next, _ => loadNested(path, qs, file, next, vm)) ).then(next, _ => loadNested(path, qs, file, next, vm));
} }
export function fetchMixin(proto) { export function fetchMixin(proto) {
let last let last;
const abort = () => last && last.abort && last.abort() const abort = () => last && last.abort && last.abort();
const request = (url, hasbar, requestHeaders) => { const request = (url, hasbar, requestHeaders) => {
abort() abort();
last = get(url, true, requestHeaders) last = get(url, true, requestHeaders);
return last return last;
} };
const get404Path = (path, config) => { const get404Path = (path, config) => {
const { notFoundPage, ext } = config const { notFoundPage, ext } = config;
const defaultPath = '_404' + (ext || '.md') const defaultPath = '_404' + (ext || '.md');
let key let key;
let path404 let path404;
switch (typeof notFoundPage) { switch (typeof notFoundPage) {
case 'boolean': case 'boolean':
path404 = defaultPath path404 = defaultPath;
break break;
case 'string': case 'string':
path404 = notFoundPage path404 = notFoundPage;
break break;
case 'object': case 'object':
key = Object.keys(notFoundPage) key = Object.keys(notFoundPage)
.sort((a, b) => b.length - a.length) .sort((a, b) => b.length - a.length)
.find(key => path.match(new RegExp('^' + key))) .find(key => path.match(new RegExp('^' + key)));
path404 = (key && notFoundPage[key]) || defaultPath path404 = (key && notFoundPage[key]) || defaultPath;
break break;
default: default:
break break;
} }
return path404 return path404;
} };
proto._loadSideAndNav = function(path, qs, loadSidebar, cb) { proto._loadSideAndNav = function(path, qs, loadSidebar, cb) {
return () => { return () => {
if (!loadSidebar) { if (!loadSidebar) {
return cb() return cb();
} }
const fn = result => { const fn = result => {
this._renderSidebar(result) this._renderSidebar(result);
cb() cb();
} };
// Load sidebar // Load sidebar
loadNested(path, qs, loadSidebar, fn, this, true) loadNested(path, qs, loadSidebar, fn, this, true);
} };
} };
proto._fetch = function(cb = noop) { proto._fetch = function(cb = noop) {
const { path, query } = this.route const { path, query } = this.route;
const qs = stringifyQuery(query, ['id']) const qs = stringifyQuery(query, ['id']);
const { loadNavbar, requestHeaders, loadSidebar } = this.config const { loadNavbar, requestHeaders, loadSidebar } = this.config;
// Abort last request // Abort last request
const file = this.router.getFile(path) const file = this.router.getFile(path);
const req = request(file + qs, true, requestHeaders) const req = request(file + qs, true, requestHeaders);
// Current page is html // Current page is html
this.isHTML = /\.html$/g.test(file) this.isHTML = /\.html$/g.test(file);
// Load main content // Load main content
req.then( req.then(
@ -95,9 +96,9 @@ export function fetchMixin(proto) {
this._loadSideAndNav(path, qs, loadSidebar, cb) this._loadSideAndNav(path, qs, loadSidebar, cb)
), ),
_ => { _ => {
this._fetchFallbackPage(file, qs, cb) || this._fetch404(file, qs, cb) this._fetchFallbackPage(file, qs, cb) || this._fetch404(file, qs, cb);
} }
) );
// Load nav // Load nav
loadNavbar && loadNavbar &&
@ -108,76 +109,78 @@ export function fetchMixin(proto) {
text => this._renderNav(text), text => this._renderNav(text),
this, this,
true true
) );
} };
proto._fetchCover = function() { proto._fetchCover = function() {
const { coverpage, requestHeaders } = this.config const { coverpage, requestHeaders } = this.config;
const query = this.route.query const query = this.route.query;
const root = getParentPath(this.route.path) const root = getParentPath(this.route.path);
if (coverpage) { if (coverpage) {
let path = null let path = null;
const routePath = this.route.path const routePath = this.route.path;
if (typeof coverpage === 'string') { if (typeof coverpage === 'string') {
if (routePath === '/') { if (routePath === '/') {
path = coverpage path = coverpage;
} }
} else if (Array.isArray(coverpage)) { } else if (Array.isArray(coverpage)) {
path = coverpage.indexOf(routePath) > -1 && '_coverpage' path = coverpage.indexOf(routePath) > -1 && '_coverpage';
} else { } else {
const cover = coverpage[routePath] const cover = coverpage[routePath];
path = cover === true ? '_coverpage' : cover path = cover === true ? '_coverpage' : cover;
} }
const coverOnly = Boolean(path) && this.config.onlyCover const coverOnly = Boolean(path) && this.config.onlyCover;
if (path) { if (path) {
path = this.router.getFile(root + path) path = this.router.getFile(root + path);
this.coverIsHTML = /\.html$/g.test(path) this.coverIsHTML = /\.html$/g.test(path);
get(path + stringifyQuery(query, ['id']), false, requestHeaders).then( get(
text => this._renderCover(text, coverOnly) path + stringifyQuery(query, ['id']),
) false,
requestHeaders
).then(text => this._renderCover(text, coverOnly));
} else { } else {
this._renderCover(null, coverOnly) this._renderCover(null, coverOnly);
} }
return coverOnly return coverOnly;
}
} }
};
proto.$fetch = function(cb = noop) { proto.$fetch = function(cb = noop) {
const done = () => { const done = () => {
callHook(this, 'doneEach') callHook(this, 'doneEach');
cb() cb();
} };
const onlyCover = this._fetchCover() const onlyCover = this._fetchCover();
if (onlyCover) { if (onlyCover) {
done() done();
} else { } else {
this._fetch(() => { this._fetch(() => {
this.$resetEvents() this.$resetEvents();
done() done();
}) });
}
} }
};
proto._fetchFallbackPage = function(path, qs, cb = noop) { proto._fetchFallbackPage = function(path, qs, cb = noop) {
const { requestHeaders, fallbackLanguages, loadSidebar } = this.config const { requestHeaders, fallbackLanguages, loadSidebar } = this.config;
if (!fallbackLanguages) { if (!fallbackLanguages) {
return false return false;
} }
const local = path.split('/')[1] const local = path.split('/')[1];
if (fallbackLanguages.indexOf(local) === -1) { if (fallbackLanguages.indexOf(local) === -1) {
return false return false;
} }
const newPath = path.replace(new RegExp(`^/${local}`), '') const newPath = path.replace(new RegExp(`^/${local}`), '');
const req = request(newPath + qs, true, requestHeaders) const req = request(newPath + qs, true, requestHeaders);
req.then( req.then(
(text, opt) => (text, opt) =>
@ -187,10 +190,10 @@ export function fetchMixin(proto) {
this._loadSideAndNav(path, qs, loadSidebar, cb) this._loadSideAndNav(path, qs, loadSidebar, cb)
), ),
() => this._fetch404(path, qs, cb) () => this._fetch404(path, qs, cb)
) );
return true return true;
} };
/** /**
* Load the 404 page * Load the 404 page
@ -201,39 +204,39 @@ export function fetchMixin(proto) {
* @private * @private
*/ */
proto._fetch404 = function(path, qs, cb = noop) { proto._fetch404 = function(path, qs, cb = noop) {
const { loadSidebar, requestHeaders, notFoundPage } = this.config const { loadSidebar, requestHeaders, notFoundPage } = this.config;
const fnLoadSideAndNav = this._loadSideAndNav(path, qs, loadSidebar, cb) const fnLoadSideAndNav = this._loadSideAndNav(path, qs, loadSidebar, cb);
if (notFoundPage) { if (notFoundPage) {
const path404 = get404Path(path, this.config) const path404 = get404Path(path, this.config);
request(this.router.getFile(path404), true, requestHeaders).then( request(this.router.getFile(path404), true, requestHeaders).then(
(text, opt) => this._renderMain(text, opt, fnLoadSideAndNav), (text, opt) => this._renderMain(text, opt, fnLoadSideAndNav),
() => this._renderMain(null, {}, fnLoadSideAndNav) () => this._renderMain(null, {}, fnLoadSideAndNav)
) );
return true return true;
} }
this._renderMain(null, {}, fnLoadSideAndNav) this._renderMain(null, {}, fnLoadSideAndNav);
return false return false;
} };
} }
export function initFetch(vm) { export function initFetch(vm) {
const { loadSidebar } = vm.config const { loadSidebar } = vm.config;
// Server-Side Rendering // Server-Side Rendering
if (vm.rendered) { if (vm.rendered) {
const activeEl = getAndActive(vm.router, '.sidebar-nav', true, true) const activeEl = getAndActive(vm.router, '.sidebar-nav', true, true);
if (loadSidebar && activeEl) { if (loadSidebar && activeEl) {
activeEl.parentNode.innerHTML += window.__SUB_SIDEBAR__ activeEl.parentNode.innerHTML += window.__SUB_SIDEBAR__;
} }
vm._bindEventOnRendered(activeEl) vm._bindEventOnRendered(activeEl);
vm.$resetEvents() vm.$resetEvents();
callHook(vm, 'doneEach') callHook(vm, 'doneEach');
callHook(vm, 'ready') callHook(vm, 'ready');
} else { } else {
vm.$fetch(_ => callHook(vm, 'ready')) vm.$fetch(_ => callHook(vm, 'ready'));
} }
} }

View File

@ -1,10 +1,10 @@
import * as util from './util' import * as util from './util';
import * as dom from './util/dom' import * as dom from './util/dom';
import {Compiler} from './render/compiler' import { Compiler } from './render/compiler';
import {slugify} from './render/slugify' import { slugify } from './render/slugify';
import {get} from './fetch/ajax' import { get } from './fetch/ajax';
import marked from 'marked' import marked from 'marked';
import prism from 'prismjs' import prism from 'prismjs';
export default function() { export default function() {
window.Docsify = { window.Docsify = {
@ -12,9 +12,9 @@ export default function () {
dom, dom,
get, get,
slugify, slugify,
version: '__VERSION__' version: '__VERSION__',
} };
window.DocsifyCompiler = Compiler window.DocsifyCompiler = Compiler;
window.marked = marked window.marked = marked;
window.Prism = prism window.Prism = prism;
} }

View File

@ -1,9 +1,9 @@
import { initMixin } from './init' import { initMixin } from './init';
import { routerMixin } from './router' import { routerMixin } from './router';
import { renderMixin } from './render' import { renderMixin } from './render';
import { fetchMixin } from './fetch' import { fetchMixin } from './fetch';
import { eventMixin } from './event' import { eventMixin } from './event';
import initGlobalAPI from './global-api' import initGlobalAPI from './global-api';
/** /**
* Fork https://github.com/bendrucker/document-ready/blob/master/index.js * Fork https://github.com/bendrucker/document-ready/blob/master/index.js
@ -12,33 +12,34 @@ import initGlobalAPI from './global-api'
* otherwise it only attaches the callback to the DOMContentLoaded event * otherwise it only attaches the callback to the DOMContentLoaded event
*/ */
function ready(callback) { function ready(callback) {
const state = document.readyState const state = document.readyState;
if (state === 'complete' || state === 'interactive') { if (state === 'complete' || state === 'interactive') {
return setTimeout(callback, 0) return setTimeout(callback, 0);
} }
document.addEventListener('DOMContentLoaded', callback) document.addEventListener('DOMContentLoaded', callback);
} }
function Docsify() { function Docsify() {
this._init() this._init();
} }
const proto = Docsify.prototype const proto = Docsify.prototype;
initMixin(proto) initMixin(proto);
routerMixin(proto) routerMixin(proto);
renderMixin(proto) renderMixin(proto);
fetchMixin(proto) fetchMixin(proto);
eventMixin(proto) eventMixin(proto);
/** /**
* Global API * Global API
*/ */
initGlobalAPI() initGlobalAPI();
/** /**
* Run Docsify * Run Docsify
*/ */
ready(_ => new Docsify()) // eslint-disable-next-line no-unused-vars
ready(_ => new Docsify());

View File

@ -1,27 +1,27 @@
import config from '../config' import config from '../config';
import {initLifecycle, callHook} from './lifecycle' import { initRender } from '../render';
import {initRender} from '../render' import { initRouter } from '../router';
import {initRouter} from '../router' import { initEvent } from '../event';
import {initEvent} from '../event' import { initFetch } from '../fetch';
import {initFetch} from '../fetch' import { isFn } from '../util/core';
import {isFn} from '../util/core' import { initLifecycle, callHook } from './lifecycle';
export function initMixin(proto) { export function initMixin(proto) {
proto._init = function() { proto._init = function() {
const vm = this const vm = this;
vm.config = config() vm.config = config();
initLifecycle(vm) // Init hooks initLifecycle(vm); // Init hooks
initPlugin(vm) // Install plugins initPlugin(vm); // Install plugins
callHook(vm, 'init') callHook(vm, 'init');
initRouter(vm) // Add router initRouter(vm); // Add router
initRender(vm) // Render base DOM initRender(vm); // Render base DOM
initEvent(vm) // Bind events initEvent(vm); // Bind events
initFetch(vm) // Fetch data initFetch(vm); // Fetch data
callHook(vm, 'mounted') callHook(vm, 'mounted');
} };
} }
function initPlugin(vm) { function initPlugin(vm) {
[].concat(vm.config.plugins).forEach(fn => isFn(fn) && fn(vm._lifecycle, vm)) [].concat(vm.config.plugins).forEach(fn => isFn(fn) && fn(vm._lifecycle, vm));
} }

View File

@ -1,4 +1,4 @@
import {noop} from '../util/core' import { noop } from '../util/core';
export function initLifecycle(vm) { export function initLifecycle(vm) {
const hooks = [ const hooks = [
@ -7,39 +7,39 @@ export function initLifecycle(vm) {
'beforeEach', 'beforeEach',
'afterEach', 'afterEach',
'doneEach', 'doneEach',
'ready' 'ready',
] ];
vm._hooks = {} vm._hooks = {};
vm._lifecycle = {} vm._lifecycle = {};
hooks.forEach(hook => { hooks.forEach(hook => {
const arr = (vm._hooks[hook] = []) const arr = (vm._hooks[hook] = []);
vm._lifecycle[hook] = fn => arr.push(fn) vm._lifecycle[hook] = fn => arr.push(fn);
}) });
} }
export function callHook(vm, hook, data, next = noop) { export function callHook(vm, hook, data, next = noop) {
const queue = vm._hooks[hook] const queue = vm._hooks[hook];
const step = function(index) { const step = function(index) {
const hook = queue[index] const hook = queue[index];
if (index >= queue.length) { if (index >= queue.length) {
next(data) next(data);
} else if (typeof hook === 'function') { } else if (typeof hook === 'function') {
if (hook.length === 2) { if (hook.length === 2) {
hook(data, result => { hook(data, result => {
data = result data = result;
step(index + 1) step(index + 1);
}) });
} else { } else {
const result = hook(data) const result = hook(data);
data = result === undefined ? data : result data = result === undefined ? data : result;
step(index + 1) step(index + 1);
} }
} else { } else {
step(index + 1) step(index + 1);
}
} }
};
step(0) step(0);
} }

View File

@ -1,21 +1,21 @@
import { tree as treeTpl } from './tpl' import { isAbsolutePath, getPath, getParentPath } from '../router/util';
import { genTree } from './gen-tree' import { isFn, merge, cached, isPrimitive } from '../util/core';
import { slugify } from './slugify' import { tree as treeTpl } from './tpl';
import { emojify } from './emojify' import { genTree } from './gen-tree';
import { isAbsolutePath, getPath, getParentPath } from '../router/util' import { slugify } from './slugify';
import { isFn, merge, cached, isPrimitive } from '../util/core' import { emojify } from './emojify';
import { imageCompiler } from './compiler/image' import { imageCompiler } from './compiler/image';
import { highlightCodeCompiler } from './compiler/code' import { highlightCodeCompiler } from './compiler/code';
import { paragraphCompiler } from './compiler/paragraph' import { paragraphCompiler } from './compiler/paragraph';
import { taskListCompiler } from './compiler/taskList' import { taskListCompiler } from './compiler/taskList';
import { taskListItemCompiler } from './compiler/taskListItem' import { taskListItemCompiler } from './compiler/taskListItem';
import { linkCompiler } from './compiler/link' import { linkCompiler } from './compiler/link';
import marked from 'marked' import marked from 'marked';
const cachedLinks = {} const cachedLinks = {};
export function getAndRemoveConfig(str = '') { export function getAndRemoveConfig(str = '') {
const config = {} const config = {};
if (str) { if (str) {
str = str str = str
@ -23,125 +23,128 @@ export function getAndRemoveConfig(str = '') {
.replace(/'$/, '') .replace(/'$/, '')
.replace(/(?:^|\s):([\w-]+:?)=?([\w-]+)?/g, (m, key, value) => { .replace(/(?:^|\s):([\w-]+:?)=?([\w-]+)?/g, (m, key, value) => {
if (key.indexOf(':') === -1) { if (key.indexOf(':') === -1) {
config[key] = (value && value.replace(/&quot;/g, '')) || true config[key] = (value && value.replace(/&quot;/g, '')) || true;
return '' return '';
} }
return m return m;
}) })
.trim() .trim();
} }
return { str, config } return { str, config };
} }
const compileMedia = { const compileMedia = {
markdown(url) { markdown(url) {
return { return {
url url,
} };
}, },
mermaid(url) { mermaid(url) {
return { return {
url url,
} };
}, },
iframe(url, title) { iframe(url, title) {
return { return {
html: `<iframe src="${url}" ${title || 'width=100% height=400'}></iframe>` html: `<iframe src="${url}" ${title ||
} 'width=100% height=400'}></iframe>`,
};
}, },
video(url, title) { video(url, title) {
return { return {
html: `<video src="${url}" ${title || 'controls'}>Not Support</video>` html: `<video src="${url}" ${title || 'controls'}>Not Support</video>`,
} };
}, },
audio(url, title) { audio(url, title) {
return { return {
html: `<audio src="${url}" ${title || 'controls'}>Not Support</audio>` html: `<audio src="${url}" ${title || 'controls'}>Not Support</audio>`,
} };
}, },
code(url, title) { code(url, title) {
let lang = url.match(/\.(\w+)$/) let lang = url.match(/\.(\w+)$/);
lang = title || (lang && lang[1]) lang = title || (lang && lang[1]);
if (lang === 'md') { if (lang === 'md') {
lang = 'markdown' lang = 'markdown';
} }
return { return {
url, url,
lang lang,
} };
} },
} };
export class Compiler { export class Compiler {
constructor(config, router) { constructor(config, router) {
this.config = config this.config = config;
this.router = router this.router = router;
this.cacheTree = {} this.cacheTree = {};
this.toc = [] this.toc = [];
this.cacheTOC = {} this.cacheTOC = {};
this.linkTarget = config.externalLinkTarget || '_blank' this.linkTarget = config.externalLinkTarget || '_blank';
this.linkRel = this.linkTarget === '_blank' ? (config.externalLinkRel || 'noopener') : '' this.linkRel =
this.contentBase = router.getBasePath() this.linkTarget === '_blank' ? config.externalLinkRel || 'noopener' : '';
this.contentBase = router.getBasePath();
const renderer = this._initRenderer() const renderer = this._initRenderer();
this.heading = renderer.heading this.heading = renderer.heading;
let compile let compile;
const mdConf = config.markdown || {} const mdConf = config.markdown || {};
if (isFn(mdConf)) { if (isFn(mdConf)) {
compile = mdConf(marked, renderer) compile = mdConf(marked, renderer);
} else { } else {
marked.setOptions( marked.setOptions(
merge(mdConf, { merge(mdConf, {
renderer: merge(renderer, mdConf.renderer) renderer: merge(renderer, mdConf.renderer),
}) })
) );
compile = marked compile = marked;
} }
this._marked = compile this._marked = compile;
this.compile = text => { this.compile = text => {
let isCached = true let isCached = true;
// eslint-disable-next-line no-unused-vars
const result = cached(_ => { const result = cached(_ => {
isCached = false isCached = false;
let html = '' let html = '';
if (!text) { if (!text) {
return text return text;
} }
if (isPrimitive(text)) { if (isPrimitive(text)) {
html = compile(text) html = compile(text);
} else { } else {
html = compile.parser(text) html = compile.parser(text);
} }
html = config.noEmoji ? html : emojify(html) html = config.noEmoji ? html : emojify(html);
slugify.clear() slugify.clear();
return html return html;
})(text) })(text);
const curFileName = this.router.parse().file const curFileName = this.router.parse().file;
if (isCached) { if (isCached) {
this.toc = this.cacheTOC[curFileName] this.toc = this.cacheTOC[curFileName];
} else { } else {
this.cacheTOC[curFileName] = [...this.toc] this.cacheTOC[curFileName] = [...this.toc];
} }
return result return result;
} };
} }
compileEmbed(href, title) { compileEmbed(href, title) {
const { str, config } = getAndRemoveConfig(title) const { str, config } = getAndRemoveConfig(title);
let embed let embed;
title = str title = str;
if (config.include) { if (config.include) {
if (!isAbsolutePath(href)) { if (!isAbsolutePath(href)) {
@ -149,55 +152,55 @@ export class Compiler {
process.env.SSR ? '' : this.contentBase, process.env.SSR ? '' : this.contentBase,
getParentPath(this.router.getCurrentPath()), getParentPath(this.router.getCurrentPath()),
href href
) );
} }
let media let media;
if (config.type && (media = compileMedia[config.type])) { if (config.type && (media = compileMedia[config.type])) {
embed = media.call(this, href, title) embed = media.call(this, href, title);
embed.type = config.type embed.type = config.type;
} else { } else {
let type = 'code' let type = 'code';
if (/\.(md|markdown)/.test(href)) { if (/\.(md|markdown)/.test(href)) {
type = 'markdown' type = 'markdown';
} else if (/\.mmd/.test(href)) { } else if (/\.mmd/.test(href)) {
type = 'mermaid' type = 'mermaid';
} else if (/\.html?/.test(href)) { } else if (/\.html?/.test(href)) {
type = 'iframe' type = 'iframe';
} else if (/\.(mp4|ogg)/.test(href)) { } else if (/\.(mp4|ogg)/.test(href)) {
type = 'video' type = 'video';
} else if (/\.mp3/.test(href)) { } else if (/\.mp3/.test(href)) {
type = 'audio' type = 'audio';
} }
embed = compileMedia[type].call(this, href, title) embed = compileMedia[type].call(this, href, title);
embed.type = type embed.type = type;
} }
embed.fragment = config.fragment embed.fragment = config.fragment;
return embed return embed;
} }
} }
_matchNotCompileLink(link) { _matchNotCompileLink(link) {
const links = this.config.noCompileLinks || [] const links = this.config.noCompileLinks || [];
for (var i = 0; i < links.length; i++) { for (let i = 0; i < links.length; i++) {
const n = links[i] const n = links[i];
const re = cachedLinks[n] || (cachedLinks[n] = new RegExp(`^${n}$`)) const re = cachedLinks[n] || (cachedLinks[n] = new RegExp(`^${n}$`));
if (re.test(link)) { if (re.test(link)) {
return link return link;
} }
} }
} }
_initRenderer() { _initRenderer() {
const renderer = new marked.Renderer() const renderer = new marked.Renderer();
const { linkTarget, router, contentBase } = this const { linkTarget, router, contentBase } = this;
const _self = this const _self = this;
const origin = {} const origin = {};
/** /**
* Render anchor tag * Render anchor tag
@ -207,39 +210,44 @@ export class Compiler {
* @returns {String} Heading element * @returns {String} Heading element
*/ */
origin.heading = renderer.heading = function(text, level) { origin.heading = renderer.heading = function(text, level) {
let { str, config } = getAndRemoveConfig(text) let { str, config } = getAndRemoveConfig(text);
const nextToc = { level, title: str } const nextToc = { level, title: str };
if (/{docsify-ignore}/g.test(str)) { if (/{docsify-ignore}/g.test(str)) {
str = str.replace('{docsify-ignore}', '') str = str.replace('{docsify-ignore}', '');
nextToc.title = str nextToc.title = str;
nextToc.ignoreSubHeading = true nextToc.ignoreSubHeading = true;
} }
if (/{docsify-ignore-all}/g.test(str)) { if (/{docsify-ignore-all}/g.test(str)) {
str = str.replace('{docsify-ignore-all}', '') str = str.replace('{docsify-ignore-all}', '');
nextToc.title = str nextToc.title = str;
nextToc.ignoreAllSubs = true nextToc.ignoreAllSubs = true;
} }
const slug = slugify(config.id || str) const slug = slugify(config.id || str);
const url = router.toURL(router.getCurrentPath(), { id: slug }) const url = router.toURL(router.getCurrentPath(), { id: slug });
nextToc.slug = url nextToc.slug = url;
_self.toc.push(nextToc) _self.toc.push(nextToc);
return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>` return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>`;
} };
origin.code = highlightCodeCompiler({ renderer }) origin.code = highlightCodeCompiler({ renderer });
origin.link = linkCompiler({ renderer, router, linkTarget, compilerClass: _self }) origin.link = linkCompiler({
origin.paragraph = paragraphCompiler({ renderer }) renderer,
origin.image = imageCompiler({ renderer, contentBase, router }) router,
origin.list = taskListCompiler({ renderer }) linkTarget,
origin.listitem = taskListItemCompiler({ renderer }) compilerClass: _self,
});
origin.paragraph = paragraphCompiler({ renderer });
origin.image = imageCompiler({ renderer, contentBase, router });
origin.list = taskListCompiler({ renderer });
origin.listitem = taskListItemCompiler({ renderer });
renderer.origin = origin renderer.origin = origin;
return renderer return renderer;
} }
/** /**
@ -249,32 +257,36 @@ export class Compiler {
* @returns {String} Sidebar element * @returns {String} Sidebar element
*/ */
sidebar(text, level) { sidebar(text, level) {
const { toc } = this const { toc } = this;
const currentPath = this.router.getCurrentPath() const currentPath = this.router.getCurrentPath();
let html = '' let html = '';
if (text) { if (text) {
html = this.compile(text) html = this.compile(text);
} else { } else {
for (let i = 0; i < toc.length; i++) { for (let i = 0; i < toc.length; i++) {
if (toc[i].ignoreSubHeading) { if (toc[i].ignoreSubHeading) {
const deletedHeaderLevel = toc[i].level const deletedHeaderLevel = toc[i].level;
toc.splice(i, 1) toc.splice(i, 1);
// Remove headers who are under current header // Remove headers who are under current header
for (let j = i; deletedHeaderLevel < toc[j].level && j < toc.length; j++) { for (
toc.splice(j, 1) && j-- && i++ let j = i;
deletedHeaderLevel < toc[j].level && j < toc.length;
j++
) {
toc.splice(j, 1) && j-- && i++;
} }
i-- i--;
} }
} }
const tree = this.cacheTree[currentPath] || genTree(toc, level) const tree = this.cacheTree[currentPath] || genTree(toc, level);
html = treeTpl(tree, '<ul>{inner}</ul>') html = treeTpl(tree, '<ul>{inner}</ul>');
this.cacheTree[currentPath] = tree this.cacheTree[currentPath] = tree;
} }
return html return html;
} }
/** /**
@ -284,33 +296,33 @@ export class Compiler {
*/ */
subSidebar(level) { subSidebar(level) {
if (!level) { if (!level) {
this.toc = [] this.toc = [];
return return;
} }
const currentPath = this.router.getCurrentPath() const currentPath = this.router.getCurrentPath();
const { cacheTree, toc } = this const { cacheTree, toc } = this;
toc[0] && toc[0].ignoreAllSubs && toc.splice(0) toc[0] && toc[0].ignoreAllSubs && toc.splice(0);
toc[0] && toc[0].level === 1 && toc.shift() toc[0] && toc[0].level === 1 && toc.shift();
for (let i = 0; i < toc.length; i++) { for (let i = 0; i < toc.length; i++) {
toc[i].ignoreSubHeading && toc.splice(i, 1) && i-- toc[i].ignoreSubHeading && toc.splice(i, 1) && i--;
} }
const tree = cacheTree[currentPath] || genTree(toc, level) const tree = cacheTree[currentPath] || genTree(toc, level);
cacheTree[currentPath] = tree cacheTree[currentPath] = tree;
this.toc = [] this.toc = [];
return treeTpl(tree) return treeTpl(tree);
} }
header(text, level) { header(text, level) {
return this.heading(text, level) return this.heading(text, level);
} }
article(text) { article(text) {
return this.compile(text) return this.compile(text);
} }
/** /**
@ -319,11 +331,11 @@ export class Compiler {
* @returns {String} Cover page * @returns {String} Cover page
*/ */
cover(text) { cover(text) {
const cacheToc = this.toc.slice() const cacheToc = this.toc.slice();
const html = this.compile(text) const html = this.compile(text);
this.toc = cacheToc.slice() this.toc = cacheToc.slice();
return html return html;
} }
} }

View File

@ -1,10 +1,14 @@
import Prism from 'prismjs' import Prism from 'prismjs';
// See https://github.com/PrismJS/prism/pull/1367 // See https://github.com/PrismJS/prism/pull/1367
import 'prismjs/components/prism-markup-templating' import 'prismjs/components/prism-markup-templating';
export const highlightCodeCompiler = ({ renderer }) => renderer.code = function (code, lang = '') { export const highlightCodeCompiler = ({ renderer }) =>
const langOrMarkup = Prism.languages[lang] || Prism.languages.markup (renderer.code = function(code, lang = '') {
const text = Prism.highlight(code.replace(/@DOCSIFY_QM@/g, '`'), langOrMarkup) const langOrMarkup = Prism.languages[lang] || Prism.languages.markup;
const text = Prism.highlight(
code.replace(/@DOCSIFY_QM@/g, '`'),
langOrMarkup
);
return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${text}</code></pre>` return `<pre v-pre data-lang="${lang}"><code class="lang-${lang}">${text}</code></pre>`;
} });

View File

@ -1,27 +1,27 @@
import { getAndRemoveConfig } from '../compiler';
import { slugify } from './slugify';
import { getAndRemoveConfig } from '../compiler' export const headingCompiler = ({ renderer, router, _self }) =>
import { slugify } from './slugify' (renderer.code = (text, level) => {
let { str, config } = getAndRemoveConfig(text);
export const headingCompiler = ({ renderer, router, _self }) => renderer.code = (text, level) => { const nextToc = { level, title: str };
let { str, config } = getAndRemoveConfig(text)
const nextToc = { level, title: str }
if (/{docsify-ignore}/g.test(str)) { if (/{docsify-ignore}/g.test(str)) {
str = str.replace('{docsify-ignore}', '') str = str.replace('{docsify-ignore}', '');
nextToc.title = str nextToc.title = str;
nextToc.ignoreSubHeading = true nextToc.ignoreSubHeading = true;
} }
if (/{docsify-ignore-all}/g.test(str)) { if (/{docsify-ignore-all}/g.test(str)) {
str = str.replace('{docsify-ignore-all}', '') str = str.replace('{docsify-ignore-all}', '');
nextToc.title = str nextToc.title = str;
nextToc.ignoreAllSubs = true nextToc.ignoreAllSubs = true;
} }
const slug = slugify(config.id || str) const slug = slugify(config.id || str);
const url = router.toURL(router.getCurrentPath(), { id: slug }) const url = router.toURL(router.getCurrentPath(), { id: slug });
nextToc.slug = url nextToc.slug = url;
_self.toc.push(nextToc) _self.toc.push(nextToc);
return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>` return `<h${level} id="${slug}"><a href="${url}" data-id="${slug}" class="anchor"><span>${str}</span></a></h${level}>`;
} });

View File

@ -1,42 +1,44 @@
import { getAndRemoveConfig } from '../compiler' import { getAndRemoveConfig } from '../compiler';
import { isAbsolutePath, getPath, getParentPath } from '../../router/util' import { isAbsolutePath, getPath, getParentPath } from '../../router/util';
export const imageCompiler = ({ renderer, contentBase, router }) => renderer.image = (href, title, text) => { export const imageCompiler = ({ renderer, contentBase, router }) =>
let url = href (renderer.image = (href, title, text) => {
let attrs = [] let url = href;
let attrs = [];
const { str, config } = getAndRemoveConfig(title) const { str, config } = getAndRemoveConfig(title);
title = str title = str;
if (config['no-zoom']) { if (config['no-zoom']) {
attrs.push('data-no-zoom') attrs.push('data-no-zoom');
} }
if (title) { if (title) {
attrs.push(`title="${title}"`) attrs.push(`title="${title}"`);
} }
if (config.size) { if (config.size) {
const [width, height] = config.size.split('x') const [width, height] = config.size.split('x');
if (height) { if (height) {
attrs.push(`width="${width}" height="${height}"`) attrs.push(`width="${width}" height="${height}"`);
} else { } else {
attrs.push(`width="${width}" height="${width}"`) attrs.push(`width="${width}" height="${width}"`);
} }
} }
if (config.class) { if (config.class) {
attrs.push(`class="${config.class}"`) attrs.push(`class="${config.class}"`);
} }
if (config.id) { if (config.id) {
attrs.push(`id="${config.id}"`) attrs.push(`id="${config.id}"`);
} }
if (!isAbsolutePath(href)) { if (!isAbsolutePath(href)) {
url = getPath(contentBase, getParentPath(router.getCurrentPath()), href) url = getPath(contentBase, getParentPath(router.getCurrentPath()), href);
}
return `<img src="${url}" data-origin="${href}" alt="${text}" ${attrs.join(' ')} />`
} }
return `<img src="${url}" data-origin="${href}" alt="${text}" ${attrs.join(
' '
)} />`;
});

View File

@ -1,42 +1,47 @@
import { getAndRemoveConfig } from '../compiler' import { getAndRemoveConfig } from '../compiler';
import { isAbsolutePath } from '../../router/util' import { isAbsolutePath } from '../../router/util';
export const linkCompiler = ({ renderer, router, linkTarget, compilerClass }) => renderer.link = (href, title = '', text) => { export const linkCompiler = ({ renderer, router, linkTarget, compilerClass }) =>
let attrs = [] (renderer.link = (href, title = '', text) => {
const { str, config } = getAndRemoveConfig(title) let attrs = [];
const { str, config } = getAndRemoveConfig(title);
title = str title = str;
if (!isAbsolutePath(href) && !compilerClass._matchNotCompileLink(href) && !config.ignore) { if (
!isAbsolutePath(href) &&
!compilerClass._matchNotCompileLink(href) &&
!config.ignore
) {
if (href === compilerClass.config.homepage) { if (href === compilerClass.config.homepage) {
href = 'README' href = 'README';
} }
href = router.toURL(href, null, router.getCurrentPath()) href = router.toURL(href, null, router.getCurrentPath());
} else { } else {
attrs.push(href.indexOf('mailto:') === 0 ? '' : `target="${linkTarget}"`) attrs.push(href.indexOf('mailto:') === 0 ? '' : `target="${linkTarget}"`);
} }
if (config.target) { if (config.target) {
attrs.push(`target="${config.target}"`) attrs.push(`target="${config.target}"`);
} }
if (config.disabled) { if (config.disabled) {
attrs.push('disabled') attrs.push('disabled');
href = 'javascript:void(0)' href = 'javascript:void(0)';
} }
if (config.class) { if (config.class) {
attrs.push(`class="${config.class}"`) attrs.push(`class="${config.class}"`);
} }
if (config.id) { if (config.id) {
attrs.push(`id="${config.id}"`) attrs.push(`id="${config.id}"`);
} }
if (title) { if (title) {
attrs.push(`title="${title}"`) attrs.push(`title="${title}"`);
} }
return `<a href="${href}" ${attrs.join(' ')}>${text}</a>` return `<a href="${href}" ${attrs.join(' ')}>${text}</a>`;
} });

View File

@ -1,15 +1,15 @@
import { helper as helperTpl } from '../tpl' import { helper as helperTpl } from '../tpl';
export const paragraphCompiler = ({ renderer }) => renderer.paragraph = text => { export const paragraphCompiler = ({ renderer }) =>
let result (renderer.paragraph = text => {
let result;
if (/^!&gt;/.test(text)) { if (/^!&gt;/.test(text)) {
result = helperTpl('tip', text) result = helperTpl('tip', text);
} else if (/^\?&gt;/.test(text)) { } else if (/^\?&gt;/.test(text)) {
result = helperTpl('warn', text) result = helperTpl('warn', text);
} else { } else {
result = `<p>${text}</p>` result = `<p>${text}</p>`;
}
return result
} }
return result;
});

View File

@ -1,13 +1,16 @@
export const taskListCompiler = ({renderer}) => renderer.list = (body, ordered, start) => { export const taskListCompiler = ({ renderer }) =>
const isTaskList = /<li class="task-list-item">/.test(body.split('class="task-list"')[0]) (renderer.list = (body, ordered, start) => {
const isStartReq = start && start > 1 const isTaskList = /<li class="task-list-item">/.test(
const tag = ordered ? 'ol' : 'ul' body.split('class="task-list"')[0]
);
const isStartReq = start && start > 1;
const tag = ordered ? 'ol' : 'ul';
const tagAttrs = [ const tagAttrs = [
(isTaskList ? 'class="task-list"' : ''), isTaskList ? 'class="task-list"' : '',
(isStartReq ? `start="${start}"` : '') isStartReq ? `start="${start}"` : '',
] ]
.join(' ') .join(' ')
.trim() .trim();
return `<${tag} ${tagAttrs}>${body}</${tag}>` return `<${tag} ${tagAttrs}>${body}</${tag}>`;
} });

View File

@ -1,6 +1,9 @@
export const taskListItemCompiler = ({ renderer }) => renderer.listitem = text => { export const taskListItemCompiler = ({ renderer }) =>
const isTaskItem = /^(<input.*type="checkbox"[^>]*>)/.test(text) (renderer.listitem = text => {
const html = isTaskItem ? `<li class="task-list-item"><label>${text}</label></li>` : `<li>${text}</li>` const isTaskItem = /^(<input.*type="checkbox"[^>]*>)/.test(text);
const html = isTaskItem
? `<li class="task-list-item"><label>${text}</label></li>`
: `<li>${text}</li>`;
return html return html;
} });

View File

@ -1,30 +1,32 @@
import { get } from '../fetch/ajax' import { get } from '../fetch/ajax';
import { merge } from '../util/core' import { merge } from '../util/core';
import stripIndent from 'strip-indent' import stripIndent from 'strip-indent';
const cached = {} const cached = {};
function walkFetchEmbed({ embedTokens, compile, fetch }, cb) { function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
let token let token;
let step = 0 let step = 0;
let count = 1 let count = 1;
if (!embedTokens.length) { if (!embedTokens.length) {
return cb({}) return cb({});
} }
while ((token = embedTokens[step++])) { while ((token = embedTokens[step++])) {
const next = (function(token) { const next = (function(token) {
return text => { return text => {
let embedToken let embedToken;
if (text) { if (text) {
if (token.embed.type === 'markdown') { if (token.embed.type === 'markdown') {
embedToken = compile.lexer(text) embedToken = compile.lexer(text);
} else if (token.embed.type === 'code') { } else if (token.embed.type === 'code') {
if (token.embed.fragment) { if (token.embed.fragment) {
const fragment = token.embed.fragment const fragment = token.embed.fragment;
const pattern = new RegExp(`(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`) const pattern = new RegExp(
text = stripIndent((text.match(pattern) || [])[1] || '').trim() `(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\[${fragment}\\]`
);
text = stripIndent((text.match(pattern) || [])[1] || '').trim();
} }
embedToken = compile.lexer( embedToken = compile.lexer(
@ -33,86 +35,86 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
'\n' + '\n' +
text.replace(/`/g, '@DOCSIFY_QM@') + text.replace(/`/g, '@DOCSIFY_QM@') +
'\n```\n' '\n```\n'
) );
} else if (token.embed.type === 'mermaid') { } else if (token.embed.type === 'mermaid') {
embedToken = [ embedToken = [
{ type: 'html', text: `<div class="mermaid">\n${text}\n</div>` } { type: 'html', text: `<div class="mermaid">\n${text}\n</div>` },
] ];
embedToken.links = {} embedToken.links = {};
} else { } else {
embedToken = [{ type: 'html', text }] embedToken = [{ type: 'html', text }];
embedToken.links = {} embedToken.links = {};
} }
} }
cb({ token, embedToken }) cb({ token, embedToken });
if (++count >= step) { if (++count >= step) {
cb({}) cb({});
} }
} };
})(token) })(token);
if (token.embed.url) { if (token.embed.url) {
if (process.env.SSR) { if (process.env.SSR) {
fetch(token.embed.url).then(next) fetch(token.embed.url).then(next);
} else { } else {
get(token.embed.url).then(next) get(token.embed.url).then(next);
} }
} else { } else {
next(token.embed.html) next(token.embed.html);
} }
} }
} }
export function prerenderEmbed({ compiler, raw = '', fetch }, done) { export function prerenderEmbed({ compiler, raw = '', fetch }, done) {
let hit = cached[raw] let hit = cached[raw];
if (hit) { if (hit) {
const copy = hit.slice() const copy = hit.slice();
copy.links = hit.links copy.links = hit.links;
return done(copy) return done(copy);
} }
const compile = compiler._marked const compile = compiler._marked;
let tokens = compile.lexer(raw) let tokens = compile.lexer(raw);
const embedTokens = [] const embedTokens = [];
const linkRE = compile.InlineLexer.rules.link const linkRE = compile.InlineLexer.rules.link;
const links = tokens.links const links = tokens.links;
tokens.forEach((token, index) => { tokens.forEach((token, index) => {
if (token.type === 'paragraph') { if (token.type === 'paragraph') {
token.text = token.text.replace( token.text = token.text.replace(
new RegExp(linkRE.source, 'g'), new RegExp(linkRE.source, 'g'),
(src, filename, href, title) => { (src, filename, href, title) => {
const embed = compiler.compileEmbed(href, title) const embed = compiler.compileEmbed(href, title);
if (embed) { if (embed) {
embedTokens.push({ embedTokens.push({
index, index,
embed embed,
}) });
} }
return src return src;
} }
) );
} }
}) });
let moveIndex = 0 let moveIndex = 0;
walkFetchEmbed({ compile, embedTokens, fetch }, ({ embedToken, token }) => { walkFetchEmbed({ compile, embedTokens, fetch }, ({ embedToken, token }) => {
if (token) { if (token) {
const index = token.index + moveIndex const index = token.index + moveIndex;
merge(links, embedToken.links) merge(links, embedToken.links);
tokens = tokens tokens = tokens
.slice(0, index) .slice(0, index)
.concat(embedToken, tokens.slice(index + 1)) .concat(embedToken, tokens.slice(index + 1));
moveIndex += embedToken.length - 1 moveIndex += embedToken.length - 1;
} else { } else {
cached[raw] = tokens.concat() cached[raw] = tokens.concat();
tokens.links = cached[raw].links = links tokens.links = cached[raw].links = links;
done(tokens) done(tokens);
} }
}) });
} }

View File

@ -1,12 +1,20 @@
import {inBrowser} from '../util/env' import { inBrowser } from '../util/env';
function replace(m, $1) { function replace(m, $1) {
return '<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/' + $1 + '.png" alt="' + $1 + '" />' return (
'<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/' +
$1 +
'.png" alt="' +
$1 +
'" />'
);
} }
export function emojify(text) { export function emojify(text) {
return text return text
.replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, m => m.replace(/:/g, '__colon__')) .replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g, m =>
.replace(/:(\w+?):/ig, (inBrowser && window.emojify) || replace) m.replace(/:/g, '__colon__')
.replace(/__colon__/g, ':') )
.replace(/:(\w+?):/gi, (inBrowser && window.emojify) || replace)
.replace(/__colon__/g, ':');
} }

View File

@ -6,25 +6,25 @@
* @return {Array} Headlines * @return {Array} Headlines
*/ */
export function genTree(toc, maxLevel) { export function genTree(toc, maxLevel) {
const headlines = [] const headlines = [];
const last = {} const last = {};
toc.forEach(headline => { toc.forEach(headline => {
const level = headline.level || 1 const level = headline.level || 1;
const len = level - 1 const len = level - 1;
if (level > maxLevel) { if (level > maxLevel) {
return return;
} }
if (last[len]) { if (last[len]) {
last[len].children = (last[len].children || []).concat(headline) last[len].children = (last[len].children || []).concat(headline);
} else { } else {
headlines.push(headline) headlines.push(headline);
} }
last[level] = headline last[level] = headline;
}) });
return headlines return headlines;
} }

View File

@ -1,53 +1,54 @@
import * as dom from '../util/dom' /* eslint-disable no-unused-vars */
import * as tpl from './tpl' import * as dom from '../util/dom';
import cssVars from '../util/polyfill/css-vars' import cssVars from '../util/polyfill/css-vars';
import tinydate from 'tinydate' import { callHook } from '../init/lifecycle';
import {callHook} from '../init/lifecycle' import { getAndActive, sticky } from '../event/sidebar';
import {Compiler} from './compiler' import { getPath, isAbsolutePath } from '../router/util';
import {getAndActive, sticky} from '../event/sidebar' import { isMobile, inBrowser } from '../util/env';
import {getPath, isAbsolutePath} from '../router/util' import { isPrimitive } from '../util/core';
import {isMobile, inBrowser} from '../util/env' import { scrollActiveSidebar, scroll2Top } from '../event/scroll';
import {isPrimitive} from '../util/core' import { Compiler } from './compiler';
import {scrollActiveSidebar, scroll2Top} from '../event/scroll' import * as tpl from './tpl';
import {prerenderEmbed} from './embed' import { prerenderEmbed } from './embed';
import tinydate from 'tinydate';
function executeScript() { function executeScript() {
const script = dom const script = dom
.findAll('.markdown-section>script') .findAll('.markdown-section>script')
.filter(s => !/template/.test(s.type))[0] .filter(s => !/template/.test(s.type))[0];
if (!script) { if (!script) {
return false return false;
} }
const code = script.innerText.trim() const code = script.innerText.trim();
if (!code) { if (!code) {
return false return false;
} }
setTimeout(_ => { setTimeout(_ => {
window.__EXECUTE_RESULT__ = new Function(code)() window.__EXECUTE_RESULT__ = new Function(code)();
}, 0) }, 0);
} }
function formatUpdated(html, updated, fn) { function formatUpdated(html, updated, fn) {
updated = updated =
typeof fn === 'function' ? typeof fn === 'function'
fn(updated) : ? fn(updated)
typeof fn === 'string' ? : typeof fn === 'string'
tinydate(fn)(new Date(updated)) : ? tinydate(fn)(new Date(updated))
updated : updated;
return html.replace(/{docsify-updated}/g, updated) return html.replace(/{docsify-updated}/g, updated);
} }
function renderMain(html) { function renderMain(html) {
if (!html) { if (!html) {
html = '<h1>404 - Not found</h1>' html = '<h1>404 - Not found</h1>';
} }
this._renderTo('.markdown-section', html) this._renderTo('.markdown-section', html);
// Render sidebar with the TOC // Render sidebar with the TOC
!this.config.loadSidebar && this._renderSidebar() !this.config.loadSidebar && this._renderSidebar();
// Execute script // Execute script
if ( if (
@ -56,229 +57,235 @@ function renderMain(html) {
!executeScript() !executeScript()
) { ) {
setTimeout(_ => { setTimeout(_ => {
const vueVM = window.__EXECUTE_RESULT__ const vueVM = window.__EXECUTE_RESULT__;
vueVM && vueVM.$destroy && vueVM.$destroy() vueVM && vueVM.$destroy && vueVM.$destroy();
window.__EXECUTE_RESULT__ = new window.Vue().$mount('#main') window.__EXECUTE_RESULT__ = new window.Vue().$mount('#main');
}, 0) }, 0);
} else { } else {
this.config.executeScript && executeScript() this.config.executeScript && executeScript();
} }
} }
function renderNameLink(vm) { function renderNameLink(vm) {
const el = dom.getNode('.app-name-link') const el = dom.getNode('.app-name-link');
const nameLink = vm.config.nameLink const nameLink = vm.config.nameLink;
const path = vm.route.path const path = vm.route.path;
if (!el) { if (!el) {
return return;
} }
if (isPrimitive(vm.config.nameLink)) { if (isPrimitive(vm.config.nameLink)) {
el.setAttribute('href', nameLink) el.setAttribute('href', nameLink);
} else if (typeof nameLink === 'object') { } else if (typeof nameLink === 'object') {
const match = Object.keys(nameLink).filter(key => path.indexOf(key) > -1)[0] const match = Object.keys(nameLink).filter(
key => path.indexOf(key) > -1
)[0];
el.setAttribute('href', nameLink[match]) el.setAttribute('href', nameLink[match]);
} }
} }
export function renderMixin(proto) { export function renderMixin(proto) {
proto._renderTo = function(el, content, replace) { proto._renderTo = function(el, content, replace) {
const node = dom.getNode(el) const node = dom.getNode(el);
if (node) { if (node) {
node[replace ? 'outerHTML' : 'innerHTML'] = content node[replace ? 'outerHTML' : 'innerHTML'] = content;
}
} }
};
proto._renderSidebar = function(text) { proto._renderSidebar = function(text) {
const {maxLevel, subMaxLevel, loadSidebar} = this.config const { maxLevel, subMaxLevel, loadSidebar } = this.config;
this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel)) this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel));
const activeEl = getAndActive(this.router, '.sidebar-nav', true, true) const activeEl = getAndActive(this.router, '.sidebar-nav', true, true);
if (loadSidebar && activeEl) { if (loadSidebar && activeEl) {
activeEl.parentNode.innerHTML += activeEl.parentNode.innerHTML +=
this.compiler.subSidebar(subMaxLevel) || '' this.compiler.subSidebar(subMaxLevel) || '';
} else { } else {
// Reset toc // Reset toc
this.compiler.subSidebar() this.compiler.subSidebar();
} }
// Bind event // Bind event
this._bindEventOnRendered(activeEl) this._bindEventOnRendered(activeEl);
} };
proto._bindEventOnRendered = function(activeEl) { proto._bindEventOnRendered = function(activeEl) {
const {autoHeader, auto2top} = this.config const { autoHeader, auto2top } = this.config;
scrollActiveSidebar(this.router) scrollActiveSidebar(this.router);
if (autoHeader && activeEl) { if (autoHeader && activeEl) {
const main = dom.getNode('#main') const main = dom.getNode('#main');
const firstNode = main.children[0] const firstNode = main.children[0];
if (firstNode && firstNode.tagName !== 'H1') { if (firstNode && firstNode.tagName !== 'H1') {
const h1 = this.compiler.header(activeEl.innerText, 1) const h1 = this.compiler.header(activeEl.innerText, 1);
const wrapper = dom.create('div', h1) const wrapper = dom.create('div', h1);
dom.before(main, wrapper.children[0]) dom.before(main, wrapper.children[0]);
} }
} }
auto2top && scroll2Top(auto2top) auto2top && scroll2Top(auto2top);
} };
proto._renderNav = function(text) { proto._renderNav = function(text) {
text && this._renderTo('nav', this.compiler.compile(text)) text && this._renderTo('nav', this.compiler.compile(text));
if (this.config.loadNavbar) { if (this.config.loadNavbar) {
getAndActive(this.router, 'nav') getAndActive(this.router, 'nav');
}
} }
};
proto._renderMain = function(text, opt = {}, next) { proto._renderMain = function(text, opt = {}, next) {
if (!text) { if (!text) {
return renderMain.call(this, text) return renderMain.call(this, text);
} }
callHook(this, 'beforeEach', text, result => { callHook(this, 'beforeEach', text, result => {
let html let html;
const callback = () => { const callback = () => {
if (opt.updatedAt) { if (opt.updatedAt) {
html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated) html = formatUpdated(html, opt.updatedAt, this.config.formatUpdated);
} }
callHook(this, 'afterEach', html, text => renderMain.call(this, text)) callHook(this, 'afterEach', html, text => renderMain.call(this, text));
} };
if (this.isHTML) { if (this.isHTML) {
html = this.result = text html = this.result = text;
callback() callback();
next() next();
} else { } else {
prerenderEmbed( prerenderEmbed(
{ {
compiler: this.compiler, compiler: this.compiler,
raw: result raw: result,
}, },
tokens => { tokens => {
html = this.compiler.compile(tokens) html = this.compiler.compile(tokens);
callback() callback();
next() next();
} }
) );
}
})
} }
});
};
proto._renderCover = function(text, coverOnly) { proto._renderCover = function(text, coverOnly) {
const el = dom.getNode('.cover') const el = dom.getNode('.cover');
dom.toggleClass(dom.getNode('main'), coverOnly ? 'add' : 'remove', 'hidden') dom.toggleClass(
dom.getNode('main'),
coverOnly ? 'add' : 'remove',
'hidden'
);
if (!text) { if (!text) {
dom.toggleClass(el, 'remove', 'show') dom.toggleClass(el, 'remove', 'show');
return return;
} }
dom.toggleClass(el, 'add', 'show') dom.toggleClass(el, 'add', 'show');
let html = this.coverIsHTML ? text : this.compiler.cover(text) let html = this.coverIsHTML ? text : this.compiler.cover(text);
const m = html const m = html
.trim() .trim()
.match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$') .match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$');
if (m) { if (m) {
if (m[2] === 'color') { if (m[2] === 'color') {
el.style.background = m[1] + (m[3] || '') el.style.background = m[1] + (m[3] || '');
} else { } else {
let path = m[1] let path = m[1];
dom.toggleClass(el, 'add', 'has-mask') dom.toggleClass(el, 'add', 'has-mask');
if (!isAbsolutePath(m[1])) { if (!isAbsolutePath(m[1])) {
path = getPath(this.router.getBasePath(), m[1]) path = getPath(this.router.getBasePath(), m[1]);
} }
el.style.backgroundImage = `url(${path})` el.style.backgroundImage = `url(${path})`;
el.style.backgroundSize = 'cover' el.style.backgroundSize = 'cover';
el.style.backgroundPosition = 'center center' el.style.backgroundPosition = 'center center';
} }
html = html.replace(m[0], '') html = html.replace(m[0], '');
} }
this._renderTo('.cover-main', html) this._renderTo('.cover-main', html);
sticky() sticky();
} };
proto._updateRender = function() { proto._updateRender = function() {
// Render name link // Render name link
renderNameLink(this) renderNameLink(this);
} };
} }
export function initRender(vm) { export function initRender(vm) {
const config = vm.config const config = vm.config;
// Init markdown compiler // Init markdown compiler
vm.compiler = new Compiler(config, vm.router) vm.compiler = new Compiler(config, vm.router);
if (inBrowser) { if (inBrowser) {
window.__current_docsify_compiler__ = vm.compiler window.__current_docsify_compiler__ = vm.compiler;
} }
const id = config.el || '#app' const id = config.el || '#app';
const navEl = dom.find('nav') || dom.create('nav') const navEl = dom.find('nav') || dom.create('nav');
const el = dom.find(id) const el = dom.find(id);
let html = '' let html = '';
let navAppendToTarget = dom.body let navAppendToTarget = dom.body;
if (el) { if (el) {
if (config.repo) { if (config.repo) {
html += tpl.corner(config.repo, config.cornerExternalLinkTarge) html += tpl.corner(config.repo, config.cornerExternalLinkTarge);
} }
if (config.coverpage) { if (config.coverpage) {
html += tpl.cover() html += tpl.cover();
} }
if (config.logo) { if (config.logo) {
const isBase64 = /^data:image/.test(config.logo) const isBase64 = /^data:image/.test(config.logo);
const isExternal = /(?:http[s]?:)?\/\//.test(config.logo) const isExternal = /(?:http[s]?:)?\/\//.test(config.logo);
const isRelative = /^\./.test(config.logo) const isRelative = /^\./.test(config.logo);
if (!isBase64 && !isExternal && !isRelative) { if (!isBase64 && !isExternal && !isRelative) {
config.logo = getPath(vm.router.getBasePath(), config.logo) config.logo = getPath(vm.router.getBasePath(), config.logo);
} }
} }
html += tpl.main(config) html += tpl.main(config);
// Render main app // Render main app
vm._renderTo(el, html, true) vm._renderTo(el, html, true);
} else { } else {
vm.rendered = true vm.rendered = true;
} }
if (config.mergeNavbar && isMobile) { if (config.mergeNavbar && isMobile) {
navAppendToTarget = dom.find('.sidebar') navAppendToTarget = dom.find('.sidebar');
} else { } else {
navEl.classList.add('app-nav') navEl.classList.add('app-nav');
if (!config.repo) { if (!config.repo) {
navEl.classList.add('no-badge') navEl.classList.add('no-badge');
} }
} }
// Add nav // Add nav
if (config.loadNavbar) { if (config.loadNavbar) {
dom.before(navAppendToTarget, navEl) dom.before(navAppendToTarget, navEl);
} }
if (config.themeColor) { if (config.themeColor) {
dom.$.head.appendChild( dom.$.head.appendChild(
dom.create('div', tpl.theme(config.themeColor)).firstElementChild dom.create('div', tpl.theme(config.themeColor)).firstElementChild
) );
// Polyfll // Polyfll
cssVars(config.themeColor) cssVars(config.themeColor);
} }
vm._updateRender() vm._updateRender();
dom.toggleClass(dom.body, 'ready') dom.toggleClass(dom.body, 'ready');
} }

View File

@ -1,42 +1,43 @@
import * as dom from '../util/dom' import * as dom from '../util/dom';
let barEl let barEl;
let timeId let timeId;
/** /**
* Init progress component * Init progress component
*/ */
function init() { function init() {
const div = dom.create('div') const div = dom.create('div');
div.classList.add('progress') div.classList.add('progress');
dom.appendTo(dom.body, div) dom.appendTo(dom.body, div);
barEl = div barEl = div;
} }
/** /**
* Render progress bar * Render progress bar
*/ */
export default function({ loaded, total, step }) { export default function({ loaded, total, step }) {
let num let num;
!barEl && init() !barEl && init();
if (step) { if (step) {
num = parseInt(barEl.style.width || 0, 10) + step num = parseInt(barEl.style.width || 0, 10) + step;
num = num > 80 ? 80 : num num = num > 80 ? 80 : num;
} else { } else {
num = Math.floor(loaded / total * 100) num = Math.floor((loaded / total) * 100);
} }
barEl.style.opacity = 1 barEl.style.opacity = 1;
barEl.style.width = num >= 95 ? '100%' : num + '%' barEl.style.width = num >= 95 ? '100%' : num + '%';
if (num >= 95) { if (num >= 95) {
clearTimeout(timeId) clearTimeout(timeId);
// eslint-disable-next-line no-unused-vars
timeId = setTimeout(_ => { timeId = setTimeout(_ => {
barEl.style.opacity = 0 barEl.style.opacity = 0;
barEl.style.width = '0%' barEl.style.width = '0%';
}, 200) }, 200);
} }
} }

View File

@ -1,15 +1,15 @@
import {hasOwn} from '../util/core' import { hasOwn } from '../util/core';
let cache = {} let cache = {};
const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g const re = /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;
function lower(string) { function lower(string) {
return string.toLowerCase() return string.toLowerCase();
} }
export function slugify(str) { export function slugify(str) {
if (typeof str !== 'string') { if (typeof str !== 'string') {
return '' return '';
} }
let slug = str let slug = str
@ -18,19 +18,19 @@ export function slugify(str) {
.replace(/<[^>\d]+>/g, '') .replace(/<[^>\d]+>/g, '')
.replace(re, '') .replace(re, '')
.replace(/\s/g, '-') .replace(/\s/g, '-')
.replace(/-+/g, '-') .replace(/-+/g, '-');
let count = cache[slug] let count = cache[slug];
count = hasOwn.call(cache, slug) ? count + 1 : 0 count = hasOwn.call(cache, slug) ? count + 1 : 0;
cache[slug] = count cache[slug] = count;
if (count) { if (count) {
slug = slug + '-' + count slug = slug + '-' + count;
} }
return slug return slug;
} }
slugify.clear = function() { slugify.clear = function() {
cache = {} cache = {};
} };

View File

@ -1,4 +1,4 @@
import { isMobile } from '../util/env' import { isMobile } from '../util/env';
/** /**
* Render github corner * Render github corner
* @param {Object} data URL for the View Source on Github link * @param {Object} data URL for the View Source on Github link
@ -7,16 +7,16 @@ import { isMobile } from '../util/env'
*/ */
export function corner(data, cornerExternalLinkTarge) { export function corner(data, cornerExternalLinkTarge) {
if (!data) { if (!data) {
return '' return '';
} }
if (!/\/\//.test(data)) { if (!/\/\//.test(data)) {
data = 'https://github.com/' + data data = 'https://github.com/' + data;
} }
data = data.replace(/^git\+/, '') data = data.replace(/^git\+/, '');
// Double check // Double check
cornerExternalLinkTarge = cornerExternalLinkTarge || '_blank' cornerExternalLinkTarge = cornerExternalLinkTarge || '_blank';
return ( return (
`<a href="${data}" target="${cornerExternalLinkTarge}" class="github-corner" aria-label="View source on Github">` + `<a href="${data}" target="${cornerExternalLinkTarge}" class="github-corner" aria-label="View source on Github">` +
@ -26,7 +26,7 @@ export function corner(data, cornerExternalLinkTarge) {
'<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>' + '<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>' +
'</svg>' + '</svg>' +
'</a>' '</a>'
) );
} }
/** /**
@ -35,7 +35,7 @@ export function corner(data, cornerExternalLinkTarge) {
* @returns {String} HTML of the main content * @returns {String} HTML of the main content
*/ */
export function main(config) { export function main(config) {
const name = config.name ? config.name : '' const name = config.name ? config.name : '';
const aside = const aside =
'<button class="sidebar-toggle" aria-label="Menu">' + '<button class="sidebar-toggle" aria-label="Menu">' +
@ -44,13 +44,13 @@ export function main(config) {
'</div>' + '</div>' +
'</button>' + '</button>' +
'<aside class="sidebar">' + '<aside class="sidebar">' +
(config.name ? (config.name
`<h1 class="app-name"><a class="app-name-link" data-nosearch>${ ? `<h1 class="app-name"><a class="app-name-link" data-nosearch>${
config.logo ? `<img alt="${name}" src=${config.logo}>` : name config.logo ? `<img alt="${name}" src=${config.logo}>` : name
}</a></h1>` : }</a></h1>`
'') + : '') +
'<div class="sidebar-nav"><!--sidebar--></div>' + '<div class="sidebar-nav"><!--sidebar--></div>' +
'</aside>' '</aside>';
return ( return (
(isMobile ? `${aside}<main>` : `<main>${aside}`) + (isMobile ? `${aside}<main>` : `<main>${aside}`) +
@ -58,7 +58,7 @@ export function main(config) {
'<article class="markdown-section" id="main"><!--main--></article>' + '<article class="markdown-section" id="main"><!--main--></article>' +
'</section>' + '</section>' +
'</main>' '</main>'
) );
} }
/** /**
@ -66,18 +66,18 @@ export function main(config) {
* @returns {String} Cover page * @returns {String} Cover page
*/ */
export function cover() { export function cover() {
const SL = ', 100%, 85%' const SL = ', 100%, 85%';
const bgc = const bgc =
'linear-gradient(to left bottom, ' + 'linear-gradient(to left bottom, ' +
`hsl(${Math.floor(Math.random() * 255) + SL}) 0%,` + `hsl(${Math.floor(Math.random() * 255) + SL}) 0%,` +
`hsl(${Math.floor(Math.random() * 255) + SL}) 100%)` `hsl(${Math.floor(Math.random() * 255) + SL}) 100%)`;
return ( return (
`<section class="cover show" style="background: ${bgc}">` + `<section class="cover show" style="background: ${bgc}">` +
'<div class="cover-main"><!--cover--></div>' + '<div class="cover-main"><!--cover--></div>' +
'<div class="mask"></div>' + '<div class="mask"></div>' +
'</section>' '</section>'
) );
} }
/** /**
@ -88,23 +88,23 @@ export function cover() {
*/ */
export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') { export function tree(toc, tpl = '<ul class="app-sub-sidebar">{inner}</ul>') {
if (!toc || !toc.length) { if (!toc || !toc.length) {
return '' return '';
} }
let innerHTML = '' let innerHTML = '';
toc.forEach(node => { toc.forEach(node => {
innerHTML += `<li><a class="section-link" href="${node.slug}">${node.title}</a></li>` innerHTML += `<li><a class="section-link" href="${node.slug}">${node.title}</a></li>`;
if (node.children) { if (node.children) {
innerHTML += tree(node.children, tpl) innerHTML += tree(node.children, tpl);
} }
}) });
return tpl.replace('{inner}', innerHTML) return tpl.replace('{inner}', innerHTML);
} }
export function helper(className, content) { export function helper(className, content) {
return `<p class="${className}">${content.slice(5).trim()}</p>` return `<p class="${className}">${content.slice(5).trim()}</p>`;
} }
export function theme(color) { export function theme(color) {
return `<style>:root{--theme-color: ${color};}</style>` return `<style>:root{--theme-color: ${color};}</style>`;
} }

View File

@ -1,25 +1,25 @@
import {History} from './base' import { parseQuery } from '../util';
import {parseQuery} from '../util' import { History } from './base';
export class AbstractHistory extends History { export class AbstractHistory extends History {
constructor(config) { constructor(config) {
super(config) super(config);
this.mode = 'abstract' this.mode = 'abstract';
} }
parse(path) { parse(path) {
let query = '' let query = '';
const queryIndex = path.indexOf('?') const queryIndex = path.indexOf('?');
if (queryIndex >= 0) { if (queryIndex >= 0) {
query = path.slice(queryIndex + 1) query = path.slice(queryIndex + 1);
path = path.slice(0, queryIndex) path = path.slice(0, queryIndex);
} }
return { return {
path, path,
file: this.getFile(path), file: this.getFile(path),
query: parseQuery(query) query: parseQuery(query),
} };
} }
} }

View File

@ -4,57 +4,59 @@ import {
stringifyQuery, stringifyQuery,
cleanPath, cleanPath,
replaceSlug, replaceSlug,
resolvePath resolvePath,
} from '../util' } from '../util';
import {noop, merge} from '../../util/core' import { noop, merge } from '../../util/core';
const cached = {} const cached = {};
function getAlias(path, alias, last) { function getAlias(path, alias, last) {
const match = Object.keys(alias).filter(key => { const match = Object.keys(alias).filter(key => {
const re = cached[key] || (cached[key] = new RegExp(`^${key}$`)) const re = cached[key] || (cached[key] = new RegExp(`^${key}$`));
return re.test(path) && path !== last return re.test(path) && path !== last;
})[0] })[0];
return match ? return match
getAlias(path.replace(cached[match], alias[match]), alias, path) : ? getAlias(path.replace(cached[match], alias[match]), alias, path)
path : path;
} }
function getFileName(path, ext) { function getFileName(path, ext) {
return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path) ? return new RegExp(`\\.(${ext.replace(/^\./, '')}|html)$`, 'g').test(path)
path : ? path
/\/$/g.test(path) ? `${path}README${ext}` : `${path}${ext}` : /\/$/g.test(path)
? `${path}README${ext}`
: `${path}${ext}`;
} }
export class History { export class History {
constructor(config) { constructor(config) {
this.config = config this.config = config;
} }
getBasePath() { getBasePath() {
return this.config.basePath return this.config.basePath;
} }
getFile(path = this.getCurrentPath(), isRelative) { getFile(path = this.getCurrentPath(), isRelative) {
const {config} = this const { config } = this;
const base = this.getBasePath() const base = this.getBasePath();
const ext = typeof config.ext === 'string' ? config.ext : '.md' const ext = typeof config.ext === 'string' ? config.ext : '.md';
path = config.alias ? getAlias(path, config.alias) : path path = config.alias ? getAlias(path, config.alias) : path;
path = getFileName(path, ext) path = getFileName(path, ext);
path = path === `/README${ext}` ? config.homepage || path : path path = path === `/README${ext}` ? config.homepage || path : path;
path = isAbsolutePath(path) ? path : getPath(base, path) path = isAbsolutePath(path) ? path : getPath(base, path);
if (isRelative) { if (isRelative) {
path = path.replace(new RegExp(`^${base}`), '') path = path.replace(new RegExp(`^${base}`), '');
} }
return path return path;
} }
onchange(cb = noop) { onchange(cb = noop) {
cb() cb();
} }
getCurrentPath() {} getCurrentPath() {}
@ -64,24 +66,28 @@ export class History {
parse() {} parse() {}
toURL(path, params, currentRoute) { toURL(path, params, currentRoute) {
const local = currentRoute && path[0] === '#' const local = currentRoute && path[0] === '#';
const route = this.parse(replaceSlug(path)) const route = this.parse(replaceSlug(path));
route.query = merge({}, route.query, params) route.query = merge({}, route.query, params);
path = route.path + stringifyQuery(route.query) path = route.path + stringifyQuery(route.query);
path = path.replace(/\.md(\?)|\.md$/, '$1') path = path.replace(/\.md(\?)|\.md$/, '$1');
if (local) { if (local) {
const idIndex = currentRoute.indexOf('?') const idIndex = currentRoute.indexOf('?');
path = path =
(idIndex > 0 ? currentRoute.substring(0, idIndex) : currentRoute) + path (idIndex > 0 ? currentRoute.substring(0, idIndex) : currentRoute) +
path;
} }
if (this.config.relativePath && path.indexOf('/') !== 0) { if (this.config.relativePath && path.indexOf('/') !== 0) {
const currentDir = currentRoute.substring(0, currentRoute.lastIndexOf('/') + 1) const currentDir = currentRoute.substring(
return cleanPath(resolvePath(currentDir + path)) 0,
currentRoute.lastIndexOf('/') + 1
);
return cleanPath(resolvePath(currentDir + path));
} }
return cleanPath('/' + path) return cleanPath('/' + path);
} }
} }

View File

@ -1,48 +1,48 @@
import { History } from './base' import { noop } from '../../util/core';
import { noop } from '../../util/core' import { on } from '../../util/dom';
import { on } from '../../util/dom' import { parseQuery, cleanPath, replaceSlug } from '../util';
import { parseQuery, cleanPath, replaceSlug } from '../util' import { History } from './base';
function replaceHash(path) { function replaceHash(path) {
const i = location.href.indexOf('#') const i = location.href.indexOf('#');
location.replace(location.href.slice(0, i >= 0 ? i : 0) + '#' + path) location.replace(location.href.slice(0, i >= 0 ? i : 0) + '#' + path);
} }
export class HashHistory extends History { export class HashHistory extends History {
constructor(config) { constructor(config) {
super(config) super(config);
this.mode = 'hash' this.mode = 'hash';
} }
getBasePath() { getBasePath() {
const path = window.location.pathname || '' const path = window.location.pathname || '';
const base = this.config.basePath const base = this.config.basePath;
return /^(\/|https?:)/g.test(base) ? base : cleanPath(path + '/' + base) return /^(\/|https?:)/g.test(base) ? base : cleanPath(path + '/' + base);
} }
getCurrentPath() { getCurrentPath() {
// We can't use location.hash here because it's not // We can't use location.hash here because it's not
// consistent across browsers - Firefox will pre-decode it! // consistent across browsers - Firefox will pre-decode it!
const href = location.href const href = location.href;
const index = href.indexOf('#') const index = href.indexOf('#');
return index === -1 ? '' : href.slice(index + 1) return index === -1 ? '' : href.slice(index + 1);
} }
onchange(cb = noop) { onchange(cb = noop) {
on('hashchange', cb) on('hashchange', cb);
} }
normalize() { normalize() {
let path = this.getCurrentPath() let path = this.getCurrentPath();
path = replaceSlug(path) path = replaceSlug(path);
if (path.charAt(0) === '/') { if (path.charAt(0) === '/') {
return replaceHash(path) return replaceHash(path);
} }
replaceHash('/' + path) replaceHash('/' + path);
} }
/** /**
@ -51,27 +51,27 @@ export class HashHistory extends History {
* @return {object} { path, query } * @return {object} { path, query }
*/ */
parse(path = location.href) { parse(path = location.href) {
let query = '' let query = '';
const hashIndex = path.indexOf('#') const hashIndex = path.indexOf('#');
if (hashIndex >= 0) { if (hashIndex >= 0) {
path = path.slice(hashIndex + 1) path = path.slice(hashIndex + 1);
} }
const queryIndex = path.indexOf('?') const queryIndex = path.indexOf('?');
if (queryIndex >= 0) { if (queryIndex >= 0) {
query = path.slice(queryIndex + 1) query = path.slice(queryIndex + 1);
path = path.slice(0, queryIndex) path = path.slice(0, queryIndex);
} }
return { return {
path, path,
file: this.getFile(path, true), file: this.getFile(path, true),
query: parseQuery(query) query: parseQuery(query),
} };
} }
toURL(path, params, currentRoute) { toURL(path, params, currentRoute) {
return '#' + super.toURL(path, params, currentRoute) return '#' + super.toURL(path, params, currentRoute);
} }
} }

View File

@ -1,38 +1,38 @@
import { History } from './base' import { noop } from '../../util/core';
import { noop } from '../../util/core' import { on } from '../../util/dom';
import { on } from '../../util/dom' import { parseQuery, getPath } from '../util';
import { parseQuery, getPath } from '../util' import { History } from './base';
export class HTML5History extends History { export class HTML5History extends History {
constructor(config) { constructor(config) {
super(config) super(config);
this.mode = 'history' this.mode = 'history';
} }
getCurrentPath() { getCurrentPath() {
const base = this.getBasePath() const base = this.getBasePath();
let path = window.location.pathname let path = window.location.pathname;
if (base && path.indexOf(base) === 0) { if (base && path.indexOf(base) === 0) {
path = path.slice(base.length) path = path.slice(base.length);
} }
return (path || '/') + window.location.search + window.location.hash return (path || '/') + window.location.search + window.location.hash;
} }
onchange(cb = noop) { onchange(cb = noop) {
on('click', e => { on('click', e => {
const el = e.target.tagName === 'A' ? e.target : e.target.parentNode const el = e.target.tagName === 'A' ? e.target : e.target.parentNode;
if (el.tagName === 'A' && !/_blank/.test(el.target)) { if (el.tagName === 'A' && !/_blank/.test(el.target)) {
e.preventDefault() e.preventDefault();
const url = el.href const url = el.href;
window.history.pushState({ key: url }, '', url) window.history.pushState({ key: url }, '', url);
cb() cb();
} }
}) });
on('popstate', cb) on('popstate', cb);
} }
/** /**
@ -41,25 +41,25 @@ export class HTML5History extends History {
* @return {object} { path, query } * @return {object} { path, query }
*/ */
parse(path = location.href) { parse(path = location.href) {
let query = '' let query = '';
const queryIndex = path.indexOf('?') const queryIndex = path.indexOf('?');
if (queryIndex >= 0) { if (queryIndex >= 0) {
query = path.slice(queryIndex + 1) query = path.slice(queryIndex + 1);
path = path.slice(0, queryIndex) path = path.slice(0, queryIndex);
} }
const base = getPath(location.origin) const base = getPath(location.origin);
const baseIndex = path.indexOf(base) const baseIndex = path.indexOf(base);
if (baseIndex > -1) { if (baseIndex > -1) {
path = path.slice(baseIndex + base.length) path = path.slice(baseIndex + base.length);
} }
return { return {
path, path,
file: this.getFile(path), file: this.getFile(path),
query: parseQuery(query) query: parseQuery(query),
} };
} }
} }

View File

@ -1,45 +1,46 @@
import {HashHistory} from './history/hash' import { supportsPushState } from '../util/env';
import {HTML5History} from './history/html5' import * as dom from '../util/dom';
import {supportsPushState} from '../util/env' import { HashHistory } from './history/hash';
import * as dom from '../util/dom' import { HTML5History } from './history/html5';
export function routerMixin(proto) { export function routerMixin(proto) {
proto.route = {} proto.route = {};
} }
let lastRoute = {} let lastRoute = {};
function updateRender(vm) { function updateRender(vm) {
vm.router.normalize() vm.router.normalize();
vm.route = vm.router.parse() vm.route = vm.router.parse();
dom.body.setAttribute('data-page', vm.route.file) dom.body.setAttribute('data-page', vm.route.file);
} }
export function initRouter(vm) { export function initRouter(vm) {
const config = vm.config const config = vm.config;
const mode = config.routerMode || 'hash' const mode = config.routerMode || 'hash';
let router let router;
if (mode === 'history' && supportsPushState) { if (mode === 'history' && supportsPushState) {
router = new HTML5History(config) router = new HTML5History(config);
} else { } else {
router = new HashHistory(config) router = new HashHistory(config);
} }
vm.router = router vm.router = router;
updateRender(vm) updateRender(vm);
lastRoute = vm.route lastRoute = vm.route;
// eslint-disable-next-line no-unused-vars
router.onchange(_ => { router.onchange(_ => {
updateRender(vm) updateRender(vm);
vm._updateRender() vm._updateRender();
if (lastRoute.path === vm.route.path) { if (lastRoute.path === vm.route.path) {
vm.$resetEvents() vm.$resetEvents();
return return;
} }
vm.$fetch() vm.$fetch();
lastRoute = vm.route lastRoute = vm.route;
}) });
} }

View File

@ -1,81 +1,81 @@
import { cached } from '../util/core' import { cached } from '../util/core';
const decode = decodeURIComponent const decode = decodeURIComponent;
const encode = encodeURIComponent const encode = encodeURIComponent;
export function parseQuery(query) { export function parseQuery(query) {
const res = {} const res = {};
query = query.trim().replace(/^(\?|#|&)/, '') query = query.trim().replace(/^(\?|#|&)/, '');
if (!query) { if (!query) {
return res return res;
} }
// Simple parse // Simple parse
query.split('&').forEach(function(param) { query.split('&').forEach(function(param) {
const parts = param.replace(/\+/g, ' ').split('=') const parts = param.replace(/\+/g, ' ').split('=');
res[parts[0]] = parts[1] && decode(parts[1]) res[parts[0]] = parts[1] && decode(parts[1]);
}) });
return res return res;
} }
export function stringifyQuery(obj, ignores = []) { export function stringifyQuery(obj, ignores = []) {
const qs = [] const qs = [];
for (const key in obj) { for (const key in obj) {
if (ignores.indexOf(key) > -1) { if (ignores.indexOf(key) > -1) {
continue continue;
} }
qs.push( qs.push(
obj[key] ? obj[key]
`${encode(key)}=${encode(obj[key])}`.toLowerCase() : ? `${encode(key)}=${encode(obj[key])}`.toLowerCase()
encode(key) : encode(key)
) );
} }
return qs.length ? `?${qs.join('&')}` : '' return qs.length ? `?${qs.join('&')}` : '';
} }
export const isAbsolutePath = cached(path => { export const isAbsolutePath = cached(path => {
return /(:|(\/{2}))/g.test(path) return /(:|(\/{2}))/g.test(path);
}) });
export const getParentPath = cached(path => { export const getParentPath = cached(path => {
if (/\/$/g.test(path)) { if (/\/$/g.test(path)) {
return path return path;
} }
const matchingParts = path.match(/(\S*\/)[^/]+$/) const matchingParts = path.match(/(\S*\/)[^/]+$/);
return matchingParts ? matchingParts[1] : '' return matchingParts ? matchingParts[1] : '';
}) });
export const cleanPath = cached(path => { export const cleanPath = cached(path => {
return path.replace(/^\/+/, '/').replace(/([^:])\/{2,}/g, '$1/') return path.replace(/^\/+/, '/').replace(/([^:])\/{2,}/g, '$1/');
}) });
export const resolvePath = cached(path => { export const resolvePath = cached(path => {
const segments = path.replace(/^\//, '').split('/') const segments = path.replace(/^\//, '').split('/');
let resolved = [] let resolved = [];
for (let i = 0, len = segments.length; i < len; i++) { for (let i = 0, len = segments.length; i < len; i++) {
const segment = segments[i] const segment = segments[i];
if (segment === '..') { if (segment === '..') {
resolved.pop() resolved.pop();
} else if (segment !== '.') { } else if (segment !== '.') {
resolved.push(segment) resolved.push(segment);
} }
} }
return '/' + resolved.join('/') return '/' + resolved.join('/');
}) });
export function getPath(...args) { export function getPath(...args) {
return cleanPath(args.join('/')) return cleanPath(args.join('/'));
} }
export const replaceSlug = cached(path => { export const replaceSlug = cached(path => {
return path.replace('#', '?id=') return path.replace('#', '?id=');
}) });

View File

@ -5,22 +5,22 @@
*/ */
export function cached(fn) { export function cached(fn) {
const cache = Object.create(null) const cache = Object.create(null);
return function(str) { return function(str) {
const key = isPrimitive(str) ? str : JSON.stringify(str) const key = isPrimitive(str) ? str : JSON.stringify(str);
const hit = cache[key] const hit = cache[key];
return hit || (cache[key] = fn(str)) return hit || (cache[key] = fn(str));
} };
} }
/** /**
* Hyphenate a camelCase string. * Hyphenate a camelCase string.
*/ */
export const hyphenate = cached(str => { export const hyphenate = cached(str => {
return str.replace(/([A-Z])/g, m => '-' + m.toLowerCase()) return str.replace(/([A-Z])/g, m => '-' + m.toLowerCase());
}) });
export const hasOwn = Object.prototype.hasOwnProperty export const hasOwn = Object.prototype.hasOwnProperty;
/** /**
* Simple Object.assign polyfill * Simple Object.assign polyfill
@ -31,17 +31,17 @@ export const merge =
Object.assign || Object.assign ||
function(to) { function(to) {
for (let i = 1; i < arguments.length; i++) { for (let i = 1; i < arguments.length; i++) {
const from = Object(arguments[i]) const from = Object(arguments[i]);
for (const key in from) { for (const key in from) {
if (hasOwn.call(from, key)) { if (hasOwn.call(from, key)) {
to[key] = from[key] to[key] = from[key];
} }
} }
} }
return to return to;
} };
/** /**
* Check if value is primitive * Check if value is primitive
@ -49,7 +49,7 @@ export const merge =
* @returns {Boolean} Result of the check * @returns {Boolean} Result of the check
*/ */
export function isPrimitive(value) { export function isPrimitive(value) {
return typeof value === 'string' || typeof value === 'number' return typeof value === 'string' || typeof value === 'number';
} }
/** /**
@ -64,6 +64,5 @@ export function noop() { }
* @returns {Boolean} True if the passed-in value is a function * @returns {Boolean} True if the passed-in value is a function
*/ */
export function isFn(obj) { export function isFn(obj) {
return typeof obj === 'function' return typeof obj === 'function';
} }

View File

@ -1,7 +1,7 @@
import { isFn } from '../util/core' import { isFn } from '../util/core';
import { inBrowser } from './env' import { inBrowser } from './env';
const cacheNode = {} const cacheNode = {};
/** /**
* Get Node * Get Node
@ -12,20 +12,20 @@ const cacheNode = {}
export function getNode(el, noCache = false) { export function getNode(el, noCache = false) {
if (typeof el === 'string') { if (typeof el === 'string') {
if (typeof window.Vue !== 'undefined') { if (typeof window.Vue !== 'undefined') {
return find(el) return find(el);
} }
el = noCache ? find(el) : cacheNode[el] || (cacheNode[el] = find(el)) el = noCache ? find(el) : cacheNode[el] || (cacheNode[el] = find(el));
} }
return el return el;
} }
export const $ = inBrowser && document export const $ = inBrowser && document;
export const body = inBrowser && $.body export const body = inBrowser && $.body;
export const head = inBrowser && $.head export const head = inBrowser && $.head;
/** /**
* Find elements * Find elements
@ -37,7 +37,7 @@ export const head = inBrowser && $.head
* find(nav, 'a') => nav.querySelector('a') * find(nav, 'a') => nav.querySelector('a')
*/ */
export function find(el, node) { export function find(el, node) {
return node ? el.querySelector(node) : $.querySelector(el) return node ? el.querySelector(node) : $.querySelector(el);
} }
/** /**
@ -52,36 +52,36 @@ export function find(el, node) {
export function findAll(el, node) { export function findAll(el, node) {
return [].slice.call( return [].slice.call(
node ? el.querySelectorAll(node) : $.querySelectorAll(el) node ? el.querySelectorAll(node) : $.querySelectorAll(el)
) );
} }
export function create(node, tpl) { export function create(node, tpl) {
node = $.createElement(node) node = $.createElement(node);
if (tpl) { if (tpl) {
node.innerHTML = tpl node.innerHTML = tpl;
} }
return node return node;
} }
export function appendTo(target, el) { export function appendTo(target, el) {
return target.appendChild(el) return target.appendChild(el);
} }
export function before(target, el) { export function before(target, el) {
return target.insertBefore(el, target.children[0]) return target.insertBefore(el, target.children[0]);
} }
export function on(el, type, handler) { export function on(el, type, handler) {
isFn(type) ? isFn(type)
window.addEventListener(el, type) : ? window.addEventListener(el, type)
el.addEventListener(type, handler) : el.addEventListener(type, handler);
} }
export function off(el, type, handler) { export function off(el, type, handler) {
isFn(type) ? isFn(type)
window.removeEventListener(el, type) : ? window.removeEventListener(el, type)
el.removeEventListener(type, handler) : el.removeEventListener(type, handler);
} }
/** /**
@ -95,9 +95,9 @@ export function off(el, type, handler) {
* toggleClass(el, 'add', 'active') => el.classList.add('active') * toggleClass(el, 'add', 'active') => el.classList.add('active')
*/ */
export function toggleClass(el, type, val) { export function toggleClass(el, type, val) {
el && el.classList[val ? type : 'toggle'](val || type) el && el.classList[val ? type : 'toggle'](val || type);
} }
export function style(content) { export function style(content) {
appendTo(head, create('style', content)) appendTo(head, create('style', content));
} }

View File

@ -1,6 +1,6 @@
export const inBrowser = !process.env.SSR export const inBrowser = !process.env.SSR;
export const isMobile = inBrowser && document.body.clientWidth <= 600 export const isMobile = inBrowser && document.body.clientWidth <= 600;
/** /**
* @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js * @see https://github.com/MoOx/pjax/blob/master/lib/is-supported.js
@ -17,5 +17,5 @@ export const supportsPushState =
!navigator.userAgent.match( !navigator.userAgent.match(
/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/ /((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/
) )
) );
})() })();

View File

@ -1,3 +1,3 @@
export * from './core' export * from './core';
export * from './env' export * from './env';
export * from '../router/util' export * from '../router/util';

View File

@ -1,36 +1,36 @@
import * as dom from '../dom' import * as dom from '../dom';
import {get} from '../../fetch/ajax' import { get } from '../../fetch/ajax';
function replaceVar(block, color) { function replaceVar(block, color) {
block.innerHTML = block.innerHTML.replace( block.innerHTML = block.innerHTML.replace(
/var\(\s*--theme-color.*?\)/g, /var\(\s*--theme-color.*?\)/g,
color color
) );
} }
export default function(color) { export default function(color) {
// Variable support // Variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--v:red)')) { if (window.CSS && window.CSS.supports && window.CSS.supports('(--v:red)')) {
return return;
} }
const styleBlocks = dom.findAll('style:not(.inserted),link'); const styleBlocks = dom.findAll('style:not(.inserted),link');
[].forEach.call(styleBlocks, block => { [].forEach.call(styleBlocks, block => {
if (block.nodeName === 'STYLE') { if (block.nodeName === 'STYLE') {
replaceVar(block, color) replaceVar(block, color);
} else if (block.nodeName === 'LINK') { } else if (block.nodeName === 'LINK') {
const href = block.getAttribute('href') const href = block.getAttribute('href');
if (!/\.css$/.test(href)) { if (!/\.css$/.test(href)) {
return return;
} }
get(href).then(res => { get(href).then(res => {
const style = dom.create('style', res) const style = dom.create('style', res);
dom.head.appendChild(style) dom.head.appendChild(style);
replaceVar(style, color) replaceVar(style, color);
}) });
} }
}) });
} }

View File

@ -1,51 +1,52 @@
const fixedPath = location.href.replace('/-/', '/#/') /* eslint-disable no-unused-vars */
const fixedPath = location.href.replace('/-/', '/#/');
if (fixedPath !== location.href) { if (fixedPath !== location.href) {
location.href = fixedPath location.href = fixedPath;
} }
function install(hook, vm) { function install(hook, vm) {
const dom = Docsify.dom const dom = Docsify.dom;
const disqus = vm.config.disqus const disqus = vm.config.disqus;
if (!disqus) { if (!disqus) {
throw Error('$docsify.disqus is required') throw Error('$docsify.disqus is required');
} }
hook.init(_ => { hook.init(_ => {
const script = dom.create('script') const script = dom.create('script');
script.async = true script.async = true;
script.src = `https://${disqus}.disqus.com/embed.js` script.src = `https://${disqus}.disqus.com/embed.js`;
script.setAttribute('data-timestamp', Number(new Date())) script.setAttribute('data-timestamp', Number(new Date()));
dom.appendTo(dom.body, script) dom.appendTo(dom.body, script);
}) });
hook.mounted(_ => { hook.mounted(_ => {
const div = dom.create('div') const div = dom.create('div');
div.id = 'disqus_thread' div.id = 'disqus_thread';
const main = dom.getNode('#main') const main = dom.getNode('#main');
div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;` div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`;
dom.appendTo(dom.find('.content'), div) dom.appendTo(dom.find('.content'), div);
// eslint-disable-next-line // eslint-disable-next-line
window.disqus_config = function() { window.disqus_config = function() {
this.page.url = location.origin + '/-' + vm.route.path this.page.url = location.origin + '/-' + vm.route.path;
this.page.identifier = vm.route.path this.page.identifier = vm.route.path;
this.page.title = document.title this.page.title = document.title;
} };
}) });
hook.doneEach(_ => { hook.doneEach(_ => {
if (typeof window.DISQUS !== 'undefined') { if (typeof window.DISQUS !== 'undefined') {
window.DISQUS.reset({ window.DISQUS.reset({
reload: true, reload: true,
config: function() { config: function() {
this.page.url = location.origin + '/-' + vm.route.path this.page.url = location.origin + '/-' + vm.route.path;
this.page.identifier = vm.route.path this.page.identifier = vm.route.path;
this.page.title = document.title this.page.title = document.title;
},
});
} }
}) });
}
})
} }
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1508,17 +1508,17 @@ const AllGithubEmoji = [
'zero', 'zero',
'zimbabwe', 'zimbabwe',
'zipper_mouth_face', 'zipper_mouth_face',
'zzz' 'zzz',
] ];
// Emoji from All-Github-Emoji-Icons // Emoji from All-Github-Emoji-Icons
// https://github.com/scotch-io/All-Github-Emoji-Icons // https://github.com/scotch-io/All-Github-Emoji-Icons
window.emojify = function(match, $1) { window.emojify = function(match, $1) {
return AllGithubEmoji.indexOf($1) === -1 ? return AllGithubEmoji.indexOf($1) === -1
match : ? match
'<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/' + : '<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/' +
$1 + $1 +
'.png" alt="' + '.png" alt="' +
$1 + $1 +
'" />' '" />';
} };

View File

@ -1,25 +1,25 @@
function handleExternalScript() { function handleExternalScript() {
const container = Docsify.dom.getNode('#main') const container = Docsify.dom.getNode('#main');
const scripts = Docsify.dom.findAll(container, 'script') const scripts = Docsify.dom.findAll(container, 'script');
for (let i = scripts.length; i--; ) { for (let i = scripts.length; i--; ) {
const script = scripts[i] const script = scripts[i];
if (script && script.src) { if (script && script.src) {
const newScript = document.createElement('script') const newScript = document.createElement('script');
Array.prototype.slice.call(script.attributes).forEach(attribute => { Array.prototype.slice.call(script.attributes).forEach(attribute => {
newScript[attribute.name] = attribute.value newScript[attribute.name] = attribute.value;
}) });
script.parentNode.insertBefore(newScript, script) script.parentNode.insertBefore(newScript, script);
script.parentNode.removeChild(script) script.parentNode.removeChild(script);
} }
} }
} }
const install = function(hook) { const install = function(hook) {
hook.doneEach(handleExternalScript) hook.doneEach(handleExternalScript);
} };
window.$docsify.plugins = [].concat(install, window.$docsify.plugins) window.$docsify.plugins = [].concat(install, window.$docsify.plugins);

View File

@ -1,13 +1,13 @@
import parser from './parser' import parser from './parser';
const install = function(hook, vm) { const install = function(hook, vm) {
hook.beforeEach(content => { hook.beforeEach(content => {
const {attributes, body} = parser(content) const { attributes, body } = parser(content);
vm.frontmatter = attributes vm.frontmatter = attributes;
return body return body;
}) });
} };
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1,39 +1,40 @@
/* eslint-disable no-console */
// From https://github.com/egoist/vue-ga/blob/master/src/index.js // From https://github.com/egoist/vue-ga/blob/master/src/index.js
function appendScript() { function appendScript() {
const script = document.createElement('script') const script = document.createElement('script');
script.async = true script.async = true;
script.src = 'https://www.google-analytics.com/analytics.js' script.src = 'https://www.google-analytics.com/analytics.js';
document.body.appendChild(script) document.body.appendChild(script);
} }
function init(id) { function init(id) {
appendScript() appendScript();
window.ga = window.ga =
window.ga || window.ga ||
function() { function() {
(window.ga.q = window.ga.q || []).push(arguments) (window.ga.q = window.ga.q || []).push(arguments);
} };
window.ga.l = Number(new Date()) window.ga.l = Number(new Date());
window.ga('create', id, 'auto') window.ga('create', id, 'auto');
} }
function collect() { function collect() {
if (!window.ga) { if (!window.ga) {
init($docsify.ga) init($docsify.ga);
} }
window.ga('set', 'page', location.hash) window.ga('set', 'page', location.hash);
window.ga('send', 'pageview') window.ga('send', 'pageview');
} }
const install = function(hook) { const install = function(hook) {
if (!$docsify.ga) { if (!$docsify.ga) {
console.error('[Docsify] ga is required.') console.error('[Docsify] ga is required.');
return return;
} }
hook.beforeEach(collect) hook.beforeEach(collect);
} };
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1,23 +1,24 @@
/* eslint-disable no-unused-vars */
function install(hook) { function install(hook) {
const dom = Docsify.dom const dom = Docsify.dom;
hook.mounted(_ => { hook.mounted(_ => {
const div = dom.create('div') const div = dom.create('div');
div.id = 'gitalk-container' div.id = 'gitalk-container';
const main = dom.getNode('#main') const main = dom.getNode('#main');
div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;` div.style = `width: ${main.clientWidth}px; margin: 0 auto 20px;`;
dom.appendTo(dom.find('.content'), div) dom.appendTo(dom.find('.content'), div);
}) });
hook.doneEach(_ => { hook.doneEach(_ => {
const el = document.getElementById('gitalk-container') const el = document.getElementById('gitalk-container');
while (el.hasChildNodes()) { while (el.hasChildNodes()) {
el.removeChild(el.firstChild) el.removeChild(el.firstChild);
} }
// eslint-disable-next-line // eslint-disable-next-line
gitalk.render('gitalk-container') gitalk.render('gitalk-container');
}) });
} }
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1,38 +1,39 @@
function appendScript(options) { function appendScript(options) {
const script = document.createElement('script') const script = document.createElement('script');
script.async = true script.async = true;
script.src = options.host + '/matomo.js' script.src = options.host + '/matomo.js';
document.body.appendChild(script) document.body.appendChild(script);
} }
function init(options) { function init(options) {
window._paq = window._paq || [] window._paq = window._paq || [];
window._paq.push(['trackPageView']) window._paq.push(['trackPageView']);
window._paq.push(['enableLinkTracking']) window._paq.push(['enableLinkTracking']);
setTimeout(function() { setTimeout(function() {
appendScript(options) appendScript(options);
window._paq.push(['setTrackerUrl', options.host + '/matomo.php']) window._paq.push(['setTrackerUrl', options.host + '/matomo.php']);
window._paq.push(['setSiteId', String(options.id)]) window._paq.push(['setSiteId', String(options.id)]);
}, 0) }, 0);
} }
function collect() { function collect() {
if (!window._paq) { if (!window._paq) {
init($docsify.matomo) init($docsify.matomo);
} }
window._paq.push(['setCustomUrl', window.location.hash.substr(1)]) window._paq.push(['setCustomUrl', window.location.hash.substr(1)]);
window._paq.push(['setDocumentTitle', document.title]) window._paq.push(['setDocumentTitle', document.title]);
window._paq.push(['trackPageView']) window._paq.push(['trackPageView']);
} }
const install = function(hook) { const install = function(hook) {
if (!$docsify.matomo) { if (!$docsify.matomo) {
console.error('[Docsify] matomo is required.') // eslint-disable-next-line no-console
return console.error('[Docsify] matomo is required.');
return;
} }
hook.beforeEach(collect) hook.beforeEach(collect);
} };
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1,7 +1,8 @@
import {search} from './search' /* eslint-disable no-unused-vars */
import { search } from './search';
let NO_DATA_TEXT = '' let NO_DATA_TEXT = '';
let options let options;
function style() { function style() {
const code = ` const code = `
@ -97,14 +98,13 @@ function style() {
.app-name.hide, .sidebar-nav.hide { .app-name.hide, .sidebar-nav.hide {
display: none; display: none;
}` }`;
Docsify.dom.style(code) Docsify.dom.style(code);
} }
function tpl(defaultValue = '') { function tpl(defaultValue = '') {
const html = const html = `<div class="input-wrap">
`<div class="input-wrap">
<input type="search" value="${defaultValue}" aria-label="Search text" /> <input type="search" value="${defaultValue}" aria-label="Search text" />
<div class="clear-button"> <div class="clear-button">
<svg width="26" height="24"> <svg width="26" height="24">
@ -115,120 +115,120 @@ function tpl(defaultValue = '') {
</div> </div>
</div> </div>
<div class="results-panel"></div> <div class="results-panel"></div>
</div>` </div>`;
const el = Docsify.dom.create('div', html) const el = Docsify.dom.create('div', html);
const aside = Docsify.dom.find('aside') const aside = Docsify.dom.find('aside');
Docsify.dom.toggleClass(el, 'search') Docsify.dom.toggleClass(el, 'search');
Docsify.dom.before(aside, el) Docsify.dom.before(aside, el);
} }
function doSearch(value) { function doSearch(value) {
const $search = Docsify.dom.find('div.search') const $search = Docsify.dom.find('div.search');
const $panel = Docsify.dom.find($search, '.results-panel') const $panel = Docsify.dom.find($search, '.results-panel');
const $clearBtn = Docsify.dom.find($search, '.clear-button') const $clearBtn = Docsify.dom.find($search, '.clear-button');
const $sidebarNav = Docsify.dom.find('.sidebar-nav') const $sidebarNav = Docsify.dom.find('.sidebar-nav');
const $appName = Docsify.dom.find('.app-name') const $appName = Docsify.dom.find('.app-name');
if (!value) { if (!value) {
$panel.classList.remove('show') $panel.classList.remove('show');
$clearBtn.classList.remove('show') $clearBtn.classList.remove('show');
$panel.innerHTML = '' $panel.innerHTML = '';
if (options.hideOtherSidebarContent) { if (options.hideOtherSidebarContent) {
$sidebarNav.classList.remove('hide') $sidebarNav.classList.remove('hide');
$appName.classList.remove('hide') $appName.classList.remove('hide');
} }
return return;
} }
const matchs = search(value) const matchs = search(value);
let html = '' let html = '';
matchs.forEach(post => { matchs.forEach(post => {
html += `<div class="matching-post"> html += `<div class="matching-post">
<a href="${post.url}"> <a href="${post.url}">
<h2>${post.title}</h2> <h2>${post.title}</h2>
<p>${post.content}</p> <p>${post.content}</p>
</a> </a>
</div>` </div>`;
}) });
$panel.classList.add('show') $panel.classList.add('show');
$clearBtn.classList.add('show') $clearBtn.classList.add('show');
$panel.innerHTML = html || `<p class="empty">${NO_DATA_TEXT}</p>` $panel.innerHTML = html || `<p class="empty">${NO_DATA_TEXT}</p>`;
if (options.hideOtherSidebarContent) { if (options.hideOtherSidebarContent) {
$sidebarNav.classList.add('hide') $sidebarNav.classList.add('hide');
$appName.classList.add('hide') $appName.classList.add('hide');
} }
} }
function bindEvents() { function bindEvents() {
const $search = Docsify.dom.find('div.search') const $search = Docsify.dom.find('div.search');
const $input = Docsify.dom.find($search, 'input') const $input = Docsify.dom.find($search, 'input');
const $inputWrap = Docsify.dom.find($search, '.input-wrap') const $inputWrap = Docsify.dom.find($search, '.input-wrap');
let timeId let timeId;
// Prevent to Fold sidebar // Prevent to Fold sidebar
Docsify.dom.on( Docsify.dom.on(
$search, $search,
'click', 'click',
e => e.target.tagName !== 'A' && e.stopPropagation() e => e.target.tagName !== 'A' && e.stopPropagation()
) );
Docsify.dom.on($input, 'input', e => { Docsify.dom.on($input, 'input', e => {
clearTimeout(timeId) clearTimeout(timeId);
timeId = setTimeout(_ => doSearch(e.target.value.trim()), 100) timeId = setTimeout(_ => doSearch(e.target.value.trim()), 100);
}) });
Docsify.dom.on($inputWrap, 'click', e => { Docsify.dom.on($inputWrap, 'click', e => {
// Click input outside // Click input outside
if (e.target.tagName !== 'INPUT') { if (e.target.tagName !== 'INPUT') {
$input.value = '' $input.value = '';
doSearch() doSearch();
} }
}) });
} }
function updatePlaceholder(text, path) { function updatePlaceholder(text, path) {
const $input = Docsify.dom.getNode('.search input[type="search"]') const $input = Docsify.dom.getNode('.search input[type="search"]');
if (!$input) { if (!$input) {
return return;
} }
if (typeof text === 'string') { if (typeof text === 'string') {
$input.placeholder = text $input.placeholder = text;
} else { } else {
const match = Object.keys(text).filter(key => path.indexOf(key) > -1)[0] const match = Object.keys(text).filter(key => path.indexOf(key) > -1)[0];
$input.placeholder = text[match] $input.placeholder = text[match];
} }
} }
function updateNoData(text, path) { function updateNoData(text, path) {
if (typeof text === 'string') { if (typeof text === 'string') {
NO_DATA_TEXT = text NO_DATA_TEXT = text;
} else { } else {
const match = Object.keys(text).filter(key => path.indexOf(key) > -1)[0] const match = Object.keys(text).filter(key => path.indexOf(key) > -1)[0];
NO_DATA_TEXT = text[match] NO_DATA_TEXT = text[match];
} }
} }
function updateOptions(opts) { function updateOptions(opts) {
options = opts options = opts;
} }
export function init(opts, vm) { export function init(opts, vm) {
const keywords = vm.router.parse().query.s const keywords = vm.router.parse().query.s;
updateOptions(opts) updateOptions(opts);
style() style();
tpl(keywords) tpl(keywords);
bindEvents() bindEvents();
keywords && setTimeout(_ => doSearch(keywords), 500) keywords && setTimeout(_ => doSearch(keywords), 500);
} }
export function update(opts, vm) { export function update(opts, vm) {
updateOptions(opts) updateOptions(opts);
updatePlaceholder(opts.placeholder, vm.route.path) updatePlaceholder(opts.placeholder, vm.route.path);
updateNoData(opts.noData, vm.route.path) updateNoData(opts.noData, vm.route.path);
} }

View File

@ -1,5 +1,6 @@
import {init as initComponet, update as updateComponent} from './component' /* eslint-disable no-unused-vars */
import {init as initSearch} from './search' import { init as initComponet, update as updateComponent } from './component';
import { init as initSearch } from './search';
const CONFIG = { const CONFIG = {
placeholder: 'Type to search', placeholder: 'Type to search',
@ -8,35 +9,36 @@ const CONFIG = {
depth: 2, depth: 2,
maxAge: 86400000, // 1 day maxAge: 86400000, // 1 day
hideOtherSidebarContent: false, hideOtherSidebarContent: false,
namespace: undefined namespace: undefined,
} };
const install = function(hook, vm) { const install = function(hook, vm) {
const {util} = Docsify const { util } = Docsify;
const opts = vm.config.search || CONFIG const opts = vm.config.search || CONFIG;
if (Array.isArray(opts)) { if (Array.isArray(opts)) {
CONFIG.paths = opts CONFIG.paths = opts;
} else if (typeof opts === 'object') { } else if (typeof opts === 'object') {
CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto' CONFIG.paths = Array.isArray(opts.paths) ? opts.paths : 'auto';
CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge CONFIG.maxAge = util.isPrimitive(opts.maxAge) ? opts.maxAge : CONFIG.maxAge;
CONFIG.placeholder = opts.placeholder || CONFIG.placeholder CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
CONFIG.noData = opts.noData || CONFIG.noData CONFIG.noData = opts.noData || CONFIG.noData;
CONFIG.depth = opts.depth || CONFIG.depth CONFIG.depth = opts.depth || CONFIG.depth;
CONFIG.hideOtherSidebarContent = opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent CONFIG.hideOtherSidebarContent =
CONFIG.namespace = opts.namespace || CONFIG.namespace opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
CONFIG.namespace = opts.namespace || CONFIG.namespace;
} }
const isAuto = CONFIG.paths === 'auto' const isAuto = CONFIG.paths === 'auto';
hook.mounted(_ => { hook.mounted(_ => {
initComponet(CONFIG, vm) initComponet(CONFIG, vm);
!isAuto && initSearch(CONFIG, vm) !isAuto && initSearch(CONFIG, vm);
}) });
hook.doneEach(_ => { hook.doneEach(_ => {
updateComponent(CONFIG, vm) updateComponent(CONFIG, vm);
isAuto && initSearch(CONFIG, vm) isAuto && initSearch(CONFIG, vm);
}) });
} };
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1,16 +1,21 @@
let INDEXS = {} /* eslint-disable no-unused-vars */
let INDEXS = {};
const LOCAL_STORAGE = { const LOCAL_STORAGE = {
EXPIRE_KEY: 'docsify.search.expires', EXPIRE_KEY: 'docsify.search.expires',
INDEX_KEY: 'docsify.search.index' INDEX_KEY: 'docsify.search.index',
} };
function resolveExpireKey(namespace) { function resolveExpireKey(namespace) {
return namespace ? `${LOCAL_STORAGE.EXPIRE_KEY}/${namespace}` : LOCAL_STORAGE.EXPIRE_KEY return namespace
? `${LOCAL_STORAGE.EXPIRE_KEY}/${namespace}`
: LOCAL_STORAGE.EXPIRE_KEY;
} }
function resolveIndexKey(namespace) { function resolveIndexKey(namespace) {
return namespace ? `${LOCAL_STORAGE.INDEX_KEY}/${namespace}` : LOCAL_STORAGE.INDEX_KEY return namespace
? `${LOCAL_STORAGE.INDEX_KEY}/${namespace}`
: LOCAL_STORAGE.INDEX_KEY;
} }
function escapeHtml(string) { function escapeHtml(string) {
@ -19,72 +24,78 @@ function escapeHtml(string) {
'<': '&lt;', '<': '&lt;',
'>': '&gt;', '>': '&gt;',
'"': '&quot;', '"': '&quot;',
'\'': '&#39;', "'": '&#39;',
'/': '&#x2F;' '/': '&#x2F;',
} };
return String(string).replace(/[&<>"'/]/g, s => entityMap[s]) return String(string).replace(/[&<>"'/]/g, s => entityMap[s]);
} }
function getAllPaths(router) { function getAllPaths(router) {
const paths = [] const paths = [];
Docsify.dom.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])').forEach(node => { Docsify.dom
const href = node.href .findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])')
const originHref = node.getAttribute('href') .forEach(node => {
const path = router.parse(href).path const href = node.href;
const originHref = node.getAttribute('href');
const path = router.parse(href).path;
if ( if (
path && path &&
paths.indexOf(path) === -1 && paths.indexOf(path) === -1 &&
!Docsify.util.isAbsolutePath(originHref) !Docsify.util.isAbsolutePath(originHref)
) { ) {
paths.push(path) paths.push(path);
} }
}) });
return paths return paths;
} }
function saveData(maxAge, expireKey, indexKey) { function saveData(maxAge, expireKey, indexKey) {
localStorage.setItem(expireKey, Date.now() + maxAge) localStorage.setItem(expireKey, Date.now() + maxAge);
localStorage.setItem(indexKey, JSON.stringify(INDEXS)) localStorage.setItem(indexKey, JSON.stringify(INDEXS));
} }
export function genIndex(path, content = '', router, depth) { export function genIndex(path, content = '', router, depth) {
const tokens = window.marked.lexer(content) const tokens = window.marked.lexer(content);
const slugify = window.Docsify.slugify const slugify = window.Docsify.slugify;
const index = {} const index = {};
let slug let slug;
tokens.forEach(token => { tokens.forEach(token => {
if (token.type === 'heading' && token.depth <= depth) { if (token.type === 'heading' && token.depth <= depth) {
slug = router.toURL(path, { id: slugify(token.text) }) slug = router.toURL(path, { id: slugify(token.text) });
index[slug] = { slug, title: token.text, body: '' } index[slug] = { slug, title: token.text, body: '' };
} else { } else {
if (!slug) { if (!slug) {
return return;
} }
if (!index[slug]) { if (!index[slug]) {
index[slug] = { slug, title: '', body: '' } index[slug] = { slug, title: '', body: '' };
} else if (index[slug].body) { } else if (index[slug].body) {
index[slug].body += '\n' + (token.text || '') index[slug].body += '\n' + (token.text || '');
} else { } else {
if (!token.text) { if (!token.text) {
if (token.type === 'table') { if (token.type === 'table') {
token.text = token.cells.map(function (rows) { token.text = token.cells
return rows.join(' | ') .map(function(rows) {
}).join(' |\n ') return rows.join(' | ');
})
.join(' |\n ');
} }
} }
index[slug].body = (index[slug].body ? index[slug].body + token.text : token.text) index[slug].body = index[slug].body
? index[slug].body + token.text
: token.text;
} }
} }
}) });
slugify.clear() slugify.clear();
return index return index;
} }
/** /**
@ -92,25 +103,25 @@ export function genIndex(path, content = '', router, depth) {
* @returns {Array} Array of results * @returns {Array} Array of results
*/ */
export function search(query) { export function search(query) {
const matchingResults = [] const matchingResults = [];
let data = [] let data = [];
Object.keys(INDEXS).forEach(key => { Object.keys(INDEXS).forEach(key => {
data = data.concat(Object.keys(INDEXS[key]).map(page => INDEXS[key][page])) data = data.concat(Object.keys(INDEXS[key]).map(page => INDEXS[key][page]));
}) });
query = query.trim() query = query.trim();
let keywords = query.split(/[\s\-\\/]+/) let keywords = query.split(/[\s\-\\/]+/);
if (keywords.length !== 1) { if (keywords.length !== 1) {
keywords = [].concat(query, keywords) keywords = [].concat(query, keywords);
} }
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
const post = data[i] const post = data[i];
let matchesScore = 0 let matchesScore = 0;
let resultStr = '' let resultStr = '';
const postTitle = post.title && post.title.trim() const postTitle = post.title && post.title.trim();
const postContent = post.body && post.body.trim() const postContent = post.body && post.body.trim();
const postUrl = post.slug || '' const postUrl = post.slug || '';
if (postTitle) { if (postTitle) {
keywords.forEach(keyword => { keywords.forEach(keyword => {
@ -118,27 +129,27 @@ export function search(query) {
const regEx = new RegExp( const regEx = new RegExp(
keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'), keyword.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
'gi' 'gi'
) );
let indexTitle = -1 let indexTitle = -1;
let indexContent = -1 let indexContent = -1;
indexTitle = postTitle ? postTitle.search(regEx) : -1 indexTitle = postTitle ? postTitle.search(regEx) : -1;
indexContent = postContent ? postContent.search(regEx) : -1 indexContent = postContent ? postContent.search(regEx) : -1;
if (indexTitle >= 0 || indexContent >= 0) { if (indexTitle >= 0 || indexContent >= 0) {
matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0 matchesScore += indexTitle >= 0 ? 3 : indexContent >= 0 ? 2 : 0;
if (indexContent < 0) { if (indexContent < 0) {
indexContent = 0 indexContent = 0;
} }
let start = 0 let start = 0;
let end = 0 let end = 0;
start = indexContent < 11 ? 0 : indexContent - 10 start = indexContent < 11 ? 0 : indexContent - 10;
end = start === 0 ? 70 : indexContent + keyword.length + 60 end = start === 0 ? 70 : indexContent + keyword.length + 60;
if (postContent && end > postContent.length) { if (postContent && end > postContent.length) {
end = postContent.length end = postContent.length;
} }
const matchContent = const matchContent =
@ -146,58 +157,58 @@ export function search(query) {
escapeHtml(postContent) escapeHtml(postContent)
.substring(start, end) .substring(start, end)
.replace(regEx, `<em class="search-keyword">${keyword}</em>`) + .replace(regEx, `<em class="search-keyword">${keyword}</em>`) +
'...' '...';
resultStr += matchContent resultStr += matchContent;
} }
}) });
if (matchesScore > 0) { if (matchesScore > 0) {
const matchingPost = { const matchingPost = {
title: escapeHtml(postTitle), title: escapeHtml(postTitle),
content: postContent ? resultStr : '', content: postContent ? resultStr : '',
url: postUrl, url: postUrl,
score: matchesScore score: matchesScore,
} };
matchingResults.push(matchingPost) matchingResults.push(matchingPost);
} }
} }
} }
return matchingResults.sort((r1, r2) => r2.score - r1.score) return matchingResults.sort((r1, r2) => r2.score - r1.score);
} }
export function init(config, vm) { export function init(config, vm) {
const isAuto = config.paths === 'auto' const isAuto = config.paths === 'auto';
const expireKey = resolveExpireKey(config.namespace) const expireKey = resolveExpireKey(config.namespace);
const indexKey = resolveIndexKey(config.namespace) const indexKey = resolveIndexKey(config.namespace);
const isExpired = localStorage.getItem(expireKey) < Date.now() const isExpired = localStorage.getItem(expireKey) < Date.now();
INDEXS = JSON.parse(localStorage.getItem(indexKey)) INDEXS = JSON.parse(localStorage.getItem(indexKey));
if (isExpired) { if (isExpired) {
INDEXS = {} INDEXS = {};
} else if (!isAuto) { } else if (!isAuto) {
return return;
} }
const paths = isAuto ? getAllPaths(vm.router) : config.paths const paths = isAuto ? getAllPaths(vm.router) : config.paths;
const len = paths.length const len = paths.length;
let count = 0 let count = 0;
paths.forEach(path => { paths.forEach(path => {
if (INDEXS[path]) { if (INDEXS[path]) {
return count++ return count++;
} }
Docsify Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then(
.get(vm.router.getFile(path), false, vm.config.requestHeaders) result => {
.then(result => { INDEXS[path] = genIndex(path, result, vm.router, config.depth);
INDEXS[path] = genIndex(path, result, vm.router, config.depth) len === ++count && saveData(config.maxAge, expireKey, indexKey);
len === ++count && saveData(config.maxAge, expireKey, indexKey) }
}) );
}) });
} }

View File

@ -1,21 +1,30 @@
import mediumZoom from 'medium-zoom' /* eslint-disable no-unused-vars */
import mediumZoom from 'medium-zoom';
const matchesSelector = Element.prototype.matches || Element.prototype.webkitMatchesSelector || Element.prototype.msMatchesSelector const matchesSelector =
Element.prototype.matches ||
Element.prototype.webkitMatchesSelector ||
Element.prototype.msMatchesSelector;
function install(hook) { function install(hook) {
let zoom let zoom;
hook.doneEach(_ => { hook.doneEach(_ => {
let elms = Array.apply(null, document.querySelectorAll('.markdown-section img:not(.emoji):not([data-no-zoom])')) let elms = Array.apply(
null,
document.querySelectorAll(
'.markdown-section img:not(.emoji):not([data-no-zoom])'
)
);
elms = elms.filter(elm => matchesSelector.call(elm, 'a img') === false) elms = elms.filter(elm => matchesSelector.call(elm, 'a img') === false);
if (zoom) { if (zoom) {
zoom.detach() zoom.detach();
} }
zoom = mediumZoom(elms) zoom = mediumZoom(elms);
}) });
} }
$docsify.plugins = [].concat(install, $docsify.plugins) $docsify.plugins = [].concat(install, $docsify.plugins);

View File

@ -1,22 +1,29 @@
/* eslint-disable no-global-assign */
// Load ES6 modules in Node.js on the fly // Load ES6 modules in Node.js on the fly
require = require('esm')(module/* , options */) /* eslint-disable-line no-global-assign */ require = require('esm')(
module /* , options */
); /* eslint-disable-line no-global-assign */
const path = require('path') const path = require('path');
const { expect } = require('chai') const { expect } = require('chai');
const { JSDOM } = require('jsdom') const { JSDOM } = require('jsdom');
function ready(callback) { function ready(callback) {
const state = document.readyState const state = document.readyState;
if (state === 'complete' || state === 'interactive') { if (state === 'complete' || state === 'interactive') {
return setTimeout(callback, 0) return setTimeout(callback, 0);
} }
document.addEventListener('DOMContentLoaded', callback) document.addEventListener('DOMContentLoaded', callback);
} }
module.exports.init = function (fixture = 'default', config = {}, markup = null) { module.exports.init = function(
fixture = 'default',
config = {},
markup = null
) {
if (markup === null || markup === undefined) { if (markup === null || markup === undefined) {
markup = `<!DOCTYPE html> markup = `<!DOCTYPE html>
<html> <html>
@ -27,64 +34,67 @@ module.exports.init = function (fixture = 'default', config = {}, markup = null)
window.$docsify = ${JSON.stringify(config, null, 2)} window.$docsify = ${JSON.stringify(config, null, 2)}
</script> </script>
</body> </body>
</html>` </html>`;
} }
const rootPath = path.join(__dirname, 'fixtures', fixture) const rootPath = path.join(__dirname, 'fixtures', fixture);
const dom = new JSDOM(markup) const dom = new JSDOM(markup);
dom.reconfigure({ url: 'file:///' + rootPath }) dom.reconfigure({ url: 'file:///' + rootPath });
global.window = dom.window global.window = dom.window;
global.document = dom.window.document global.document = dom.window.document;
global.navigator = dom.window.navigator global.navigator = dom.window.navigator;
global.location = dom.window.location global.location = dom.window.location;
global.XMLHttpRequest = dom.window.XMLHttpRequest global.XMLHttpRequest = dom.window.XMLHttpRequest;
// Mimic src/core/index.js but for Node.js // Mimic src/core/index.js but for Node.js
function Docsify() { function Docsify() {
this._init() this._init();
} }
const proto = Docsify.prototype const proto = Docsify.prototype;
const { initMixin } = require('../src/core/init') const { initMixin } = require('../src/core/init');
const { routerMixin } = require('../src/core//router') const { routerMixin } = require('../src/core//router');
const { renderMixin } = require('../src/core//render') const { renderMixin } = require('../src/core//render');
const { fetchMixin } = require('../src/core/fetch') const { fetchMixin } = require('../src/core/fetch');
const { eventMixin } = require('../src/core//event') const { eventMixin } = require('../src/core//event');
initMixin(proto) initMixin(proto);
routerMixin(proto) routerMixin(proto);
renderMixin(proto) renderMixin(proto);
fetchMixin(proto) fetchMixin(proto);
eventMixin(proto) eventMixin(proto);
const NOT_INIT_PATTERN = '<!--main-->' const NOT_INIT_PATTERN = '<!--main-->';
return new Promise(resolve => { return new Promise(resolve => {
ready(() => { ready(() => {
const docsify = new Docsify() const docsify = new Docsify();
// NOTE: I was not able to get it working with a callback, but polling works usually at the first time // NOTE: I was not able to get it working with a callback, but polling works usually at the first time
const id = setInterval(() => { const id = setInterval(() => {
if (dom.window.document.body.innerHTML.indexOf(NOT_INIT_PATTERN) === -1) { if (
clearInterval(id) dom.window.document.body.innerHTML.indexOf(NOT_INIT_PATTERN) === -1
) {
clearInterval(id);
return resolve({ return resolve({
docsify: docsify, docsify: docsify,
dom: dom dom: dom,
}) });
}
}, 10)
})
})
} }
}, 10);
});
});
};
module.exports.expectSameDom = function(actual, expected) { module.exports.expectSameDom = function(actual, expected) {
const WHITESPACES_BETWEEN_TAGS = />(\s\s+)</g const WHITESPACES_BETWEEN_TAGS = />(\s\s+)</g;
function replacer(match, group1) { function replacer(match, group1) {
return match.replace(group1, '') return match.replace(group1, '');
} }
expect(actual.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim()) expect(actual.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim()).equal(
.equal(expected.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim()) expected.replace(WHITESPACES_BETWEEN_TAGS, replacer).trim()
} );
};

View File

@ -1,9 +1,9 @@
const { init } = require('../_helper') // const { init } = require('../_helper');
describe('full docsify initialization', function() { describe('full docsify initialization', function() {
it('TODO: check generated markup', async function() { it('TODO: check generated markup', async function() {
const { dom } = await init('simple', { loadSidebar: true }) // const { dom } = await init('simple', { loadSidebar: true });
console.log(dom.window.document.body.innerHTML) // console.log(dom.window.document.body.innerHTML);
// TODO: add some expectations // TODO: add some expectations
}) });
}) });

View File

@ -1,9 +1,9 @@
const { init } = require('../_helper') const { init } = require('../_helper');
describe('router', function() { describe('router', function() {
it('TODO: trigger to load another page', async function() { it('TODO: trigger to load another page', async function() {
await init() await init();
window.location = '/?foo=bar' window.location = '/?foo=bar';
// TODO: add some expectations // TODO: add some expectations
}) });
}) });

View File

@ -1,61 +1,64 @@
require = require('esm')(module/* , options */) /* eslint-disable-line no-global-assign */ /* eslint-disable no-global-assign */
const { expect } = require('chai') require = require('esm')(
const { History } = require('../../src/core/router/history/base') module /* , options */
); /* eslint-disable-line no-global-assign */
const { History } = require('../../src/core/router/history/base');
const { expect } = require('chai');
class MockHistory extends History { class MockHistory extends History {
parse(path) { parse(path) {
return { path } return { path };
} }
} }
describe('router/history/base', function() { describe('router/history/base', function() {
describe('relativePath true', function() { describe('relativePath true', function() {
var history let history;
beforeEach(function() { beforeEach(function() {
history = new MockHistory({ relativePath: true }) history = new MockHistory({ relativePath: true });
}) });
it('toURL', function() { it('toURL', function() {
// WHEN // WHEN
const url = history.toURL('guide.md', {}, '/zh-ch/') const url = history.toURL('guide.md', {}, '/zh-ch/');
// THEN // THEN
expect(url).equal('/zh-ch/guide') expect(url).equal('/zh-ch/guide');
}) });
it('toURL with double dot', function() { it('toURL with double dot', function() {
// WHEN // WHEN
const url = history.toURL('../README.md', {}, '/zh-ch/') const url = history.toURL('../README.md', {}, '/zh-ch/');
// THEN // THEN
expect(url).equal('/README') expect(url).equal('/README');
}) });
it('toURL child path', function() { it('toURL child path', function() {
// WHEN // WHEN
const url = history.toURL('config/example.md', {}, '/zh-ch/') const url = history.toURL('config/example.md', {}, '/zh-ch/');
// THEN // THEN
expect(url).equal('/zh-ch/config/example') expect(url).equal('/zh-ch/config/example');
}) });
it('toURL absolute path', function() { it('toURL absolute path', function() {
// WHEN // WHEN
const url = history.toURL('/README', {}, '/zh-ch/') const url = history.toURL('/README', {}, '/zh-ch/');
// THEN // THEN
expect(url).equal('/README') expect(url).equal('/README');
}) });
}) });
it('toURL without relative path', function() { it('toURL without relative path', function() {
const history = new MockHistory({ relativePath: false }) const history = new MockHistory({ relativePath: false });
// WHEN // WHEN
const url = history.toURL('README', {}, '/zh-ch/') const url = history.toURL('README', {}, '/zh-ch/');
// THEN // THEN
expect(url).equal('/README') expect(url).equal('/README');
}) });
}) });

View File

@ -1,78 +1,93 @@
const { expect } = require('chai') const { init, expectSameDom } = require('../_helper');
const { expect } = require('chai');
const { init, expectSameDom } = require('../_helper')
describe('render', function() { describe('render', function() {
it('important content (tips)', async function() { it('important content (tips)', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('!> **Time** is money, my friend!') const output = docsify.compiler.compile('!> **Time** is money, my friend!');
expect(output).equal('<p class="tip"><strong>Time</strong> is money, my friend!</p>') expect(output).equal(
}) '<p class="tip"><strong>Time</strong> is money, my friend!</p>'
);
});
describe('lists', function() { describe('lists', function() {
it('as unordered task list', async function() { it('as unordered task list', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile(` const output = docsify.compiler.compile(`
- [x] Task 1 - [x] Task 1
- [ ] Task 2 - [ ] Task 2
- [ ] Task 3`) - [ ] Task 3`);
expect(output, `<ul class="task-list"> expect(
output,
`<ul class="task-list">
<li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 1</label></li> <li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 1</label></li>
<li class="task-list-item"><label><input disabled="" type="checkbox"> Task 2</label></li> <li class="task-list-item"><label><input disabled="" type="checkbox"> Task 2</label></li>
<li class="task-list-item"><label><input disabled="" type="checkbox"> Task 3</label></li> <li class="task-list-item"><label><input disabled="" type="checkbox"> Task 3</label></li>
</ul>`) </ul>`
}) );
});
it('as ordered task list', async function() { it('as ordered task list', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile(` const output = docsify.compiler.compile(`
1. [ ] Task 1 1. [ ] Task 1
2. [x] Task 2`) 2. [x] Task 2`);
expectSameDom(output, `<ol class="task-list"> expectSameDom(
output,
`<ol class="task-list">
<li class="task-list-item"><label><input disabled="" type="checkbox"> Task 1</label></li> <li class="task-list-item"><label><input disabled="" type="checkbox"> Task 1</label></li>
<li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 2</label></li> <li class="task-list-item"><label><input checked="" disabled="" type="checkbox"> Task 2</label></li>
</ol>`) </ol>`
}) );
});
it('normal unordered', async function() { it('normal unordered', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile(` const output = docsify.compiler.compile(`
- [linktext](link) - [linktext](link)
- just text`) - just text`);
expectSameDom(output, `<ul > expectSameDom(
output,
`<ul >
<li><a href="#/link" >linktext</a></li> <li><a href="#/link" >linktext</a></li>
<li>just text</li> <li>just text</li>
</ul>`) </ul>`
}) );
});
it('unordered with custom start', async function() { it('unordered with custom start', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile(` const output = docsify.compiler.compile(`
1. first 1. first
2. second 2. second
text text
3. third`) 3. third`);
expectSameDom(output, `<ol > expectSameDom(
output,
`<ol >
<li>first</li> <li>first</li>
<li>second</li> <li>second</li>
</ol> </ol>
<p>text</p> <p>text</p>
<ol start="3"> <ol start="3">
<li>third</li> <li>third</li>
</ol>`) </ol>`
}) );
});
it('nested', async function() { it('nested', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile(` const output = docsify.compiler.compile(`
- 1 - 1
- 2 - 2
- 2 a - 2 a
- 2 b - 2 b
- 3`) - 3`);
expectSameDom(output, `<ul > expectSameDom(
output,
`<ul >
<li>1</li> <li>1</li>
<li>2<ul > <li>2<ul >
<li>2 a</li> <li>2 a</li>
@ -80,158 +95,228 @@ text
</ul> </ul>
</li> </li>
<li>3</li> <li>3</li>
</ul>`) </ul>`
}) );
}) });
});
describe('image', function() { describe('image', function() {
it('regular', async function() { it('regular', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('![alt text](http://imageUrl)') const output = docsify.compiler.compile('![alt text](http://imageUrl)');
expectSameDom(output, '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" /></p>') expectSameDom(
}) output,
'<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" /></p>'
);
});
it('class', async function() { it('class', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('![alt text](http://imageUrl \':class=someCssClass\')') const output = docsify.compiler.compile(
"![alt text](http://imageUrl ':class=someCssClass')"
);
expectSameDom(output, '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" class="someCssClass" /></p>') expectSameDom(
}) output,
'<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" class="someCssClass" /></p>'
);
});
it('id', async function() { it('id', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('![alt text](http://imageUrl \':id=someCssID\')') const output = docsify.compiler.compile(
"![alt text](http://imageUrl ':id=someCssID')"
);
expectSameDom(output, '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" id="someCssID" /></p>') expectSameDom(
}) output,
'<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" id="someCssID" /></p>'
);
});
it('no-zoom', async function() { it('no-zoom', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('![alt text](http://imageUrl \':no-zoom\')') const output = docsify.compiler.compile(
"![alt text](http://imageUrl ':no-zoom')"
);
expectSameDom(output, '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" data-no-zoom /></p>') expectSameDom(
}) output,
'<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" data-no-zoom /></p>'
);
});
describe('size', function() { describe('size', function() {
it('width and height', async function() { it('width and height', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('![alt text](http://imageUrl \':size=WIDTHxHEIGHT\')') const output = docsify.compiler.compile(
"![alt text](http://imageUrl ':size=WIDTHxHEIGHT')"
);
expectSameDom(output, '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="WIDTH" height="HEIGHT" /></p>') expectSameDom(
}) output,
'<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="WIDTH" height="HEIGHT" /></p>'
);
});
it('width', async function() { it('width', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('![alt text](http://imageUrl \':size=50\')') const output = docsify.compiler.compile(
"![alt text](http://imageUrl ':size=50')"
);
expectSameDom(output, '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="50" height="50" /></p>') expectSameDom(
}) output,
}) '<p><img src="http://imageUrl" data-origin="http://imageUrl" alt="alt text" width="50" height="50" /></p>'
}) );
});
});
});
describe('heading', function() { describe('heading', function() {
it('h1', async function() { it('h1', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('# h1 tag') const output = docsify.compiler.compile('# h1 tag');
expectSameDom(output, ` expectSameDom(
output,
`
<h1 id="h1-tag"> <h1 id="h1-tag">
<a href="#/?id=h1-tag" data-id="h1-tag" class="anchor"> <a href="#/?id=h1-tag" data-id="h1-tag" class="anchor">
<span>h1 tag</span> <span>h1 tag</span>
</a> </a>
</h1>`) </h1>`
}) );
});
it('h2', async function() { it('h2', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('## h2 tag') const output = docsify.compiler.compile('## h2 tag');
expectSameDom(output, ` expectSameDom(
output,
`
<h2 id="h2-tag"> <h2 id="h2-tag">
<a href="#/?id=h2-tag" data-id="h2-tag" class="anchor"> <a href="#/?id=h2-tag" data-id="h2-tag" class="anchor">
<span>h2 tag</span> <span>h2 tag</span>
</a> </a>
</h2>`) </h2>`
}) );
});
it('h3', async function() { it('h3', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('### h3 tag') const output = docsify.compiler.compile('### h3 tag');
expectSameDom(output, ` expectSameDom(
output,
`
<h3 id="h3-tag"> <h3 id="h3-tag">
<a href="#/?id=h3-tag" data-id="h3-tag" class="anchor"> <a href="#/?id=h3-tag" data-id="h3-tag" class="anchor">
<span>h3 tag</span> <span>h3 tag</span>
</a> </a>
</h3>`) </h3>`
}) );
});
it('h4', async function() { it('h4', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('#### h4 tag') const output = docsify.compiler.compile('#### h4 tag');
expectSameDom(output, ` expectSameDom(
output,
`
<h4 id="h4-tag"> <h4 id="h4-tag">
<a href="#/?id=h4-tag" data-id="h4-tag" class="anchor"> <a href="#/?id=h4-tag" data-id="h4-tag" class="anchor">
<span>h4 tag</span> <span>h4 tag</span>
</a> </a>
</h4>`) </h4>`
}) );
});
it('h5', async function() { it('h5', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('##### h5 tag') const output = docsify.compiler.compile('##### h5 tag');
expectSameDom(output, ` expectSameDom(
output,
`
<h5 id="h5-tag"> <h5 id="h5-tag">
<a href="#/?id=h5-tag" data-id="h5-tag" class="anchor"> <a href="#/?id=h5-tag" data-id="h5-tag" class="anchor">
<span>h5 tag</span> <span>h5 tag</span>
</a> </a>
</h5>`) </h5>`
}) );
});
it('h6', async function() { it('h6', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('###### h6 tag') const output = docsify.compiler.compile('###### h6 tag');
expectSameDom(output, ` expectSameDom(
output,
`
<h6 id="h6-tag"> <h6 id="h6-tag">
<a href="#/?id=h6-tag" data-id="h6-tag" class="anchor"> <a href="#/?id=h6-tag" data-id="h6-tag" class="anchor">
<span>h6 tag</span> <span>h6 tag</span>
</a> </a>
</h6>`) </h6>`
}) );
}) });
});
describe('link', function() { describe('link', function() {
it('regular', async function() { it('regular', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('[alt text](http://url)') const output = docsify.compiler.compile('[alt text](http://url)');
expectSameDom(output, '<p><a href="http://url" target="_blank">alt text</a></p>') expectSameDom(
}) output,
'<p><a href="http://url" target="_blank">alt text</a></p>'
);
});
it('disabled', async function() { it('disabled', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('[alt text](http://url \':disabled\')') const output = docsify.compiler.compile(
"[alt text](http://url ':disabled')"
);
expectSameDom(output, '<p><a href="javascript:void(0)" target="_blank" disabled>alt text</a></p>') expectSameDom(
}) output,
'<p><a href="javascript:void(0)" target="_blank" disabled>alt text</a></p>'
);
});
it('target', async function() { it('target', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('[alt text](http://url \':target=_self\')') const output = docsify.compiler.compile(
"[alt text](http://url ':target=_self')"
);
expectSameDom(output, '<p><a href="http://url" target="_blank" target="_self">alt text</a></p>') expectSameDom(
}) output,
'<p><a href="http://url" target="_blank" target="_self">alt text</a></p>'
);
});
it('class', async function() { it('class', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('[alt text](http://url \':class=someCssClass\')') const output = docsify.compiler.compile(
"[alt text](http://url ':class=someCssClass')"
);
expectSameDom(output, '<p><a href="http://url" target="_blank" class="someCssClass">alt text</a></p>') expectSameDom(
}) output,
'<p><a href="http://url" target="_blank" class="someCssClass">alt text</a></p>'
);
});
it('id', async function() { it('id', async function() {
const { docsify } = await init() const { docsify } = await init();
const output = docsify.compiler.compile('[alt text](http://url \':id=someCssID\')') const output = docsify.compiler.compile(
"[alt text](http://url ':id=someCssID')"
);
expectSameDom(output, '<p><a href="http://url" target="_blank" id="someCssID">alt text</a></p>') expectSameDom(
}) output,
}) '<p><a href="http://url" target="_blank" id="someCssID">alt text</a></p>'
}) );
});
});
});

View File

@ -1,29 +1,32 @@
require = require('esm')(module/* , options */) /* eslint-disable-line no-global-assign */ /* eslint-disable no-global-assign */
const { expect } = require('chai') require = require('esm')(
const { resolvePath } = require('../../src/core/router/util') module /* , options */
); /* eslint-disable-line no-global-assign */
const { resolvePath } = require('../../src/core/router/util');
const { expect } = require('chai');
describe('router/util', function() { describe('router/util', function() {
it('resolvePath', async function() { it('resolvePath', async function() {
// WHEN // WHEN
const result = resolvePath('hello.md') const result = resolvePath('hello.md');
// THEN // THEN
expect(result).equal('/hello.md') expect(result).equal('/hello.md');
}) });
it('resolvePath with dot', async function() { it('resolvePath with dot', async function() {
// WHEN // WHEN
const result = resolvePath('./hello.md') const result = resolvePath('./hello.md');
// THEN // THEN
expect(result).equal('/hello.md') expect(result).equal('/hello.md');
}) });
it('resolvePath with two dots', async function() { it('resolvePath with two dots', async function() {
// WHEN // WHEN
const result = resolvePath('test/../hello.md') const result = resolvePath('test/../hello.md');
// THEN // THEN
expect(result).equal('/hello.md') expect(result).equal('/hello.md');
}) });
}) });