Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bf2368892 | ||
|
|
ca4b8aa94b | ||
|
|
842146183d | ||
|
|
d52f605200 | ||
|
|
eb6aedf5cc | ||
|
|
0eee81b048 | ||
|
|
09d579b516 | ||
|
|
2c3ca5aeb4 | ||
|
|
eb66e7f894 | ||
|
|
968516a9c8 | ||
|
|
aaa3c8d6b4 | ||
|
|
ac2f924dad | ||
|
|
c6e5159cc6 | ||
|
|
2823b2b53d | ||
|
|
22a2f084a1 | ||
|
|
672a39b697 | ||
|
|
8f48f0bce6 | ||
|
|
de3bd8d665 | ||
|
|
ad39b98734 | ||
|
|
26b83b0365 | ||
|
|
0c7f500c41 | ||
|
|
e55aa3bc77 | ||
|
|
43a994245e | ||
|
|
bf439d34b7 | ||
|
|
604be71a37 | ||
|
|
309a951b93 | ||
|
|
0f7782a321 | ||
|
|
521ef0b8ee | ||
|
|
a9e7a5d421 | ||
|
|
7908140dc5 | ||
|
|
07e2eb4f85 | ||
|
|
af4db15a27 | ||
|
|
3e50c3394a | ||
|
|
5765619416 | ||
|
|
6fcf8d2ec1 | ||
|
|
5b92b29dc4 | ||
|
|
c59d776031 | ||
|
|
dccfcb8732 | ||
|
|
281449f418 | ||
|
|
b7fa2448a4 | ||
|
|
8f5aa8ad5f | ||
|
|
4927e57c88 | ||
|
|
9b471c682f | ||
|
|
18262ce8d2 | ||
|
|
769727e886 | ||
|
|
99bc558d5c | ||
|
|
b33a92e37b | ||
|
|
eadce61e75 | ||
|
|
13ab32f631 | ||
|
|
e483055e17 | ||
|
|
c890255c39 | ||
|
|
5e5b680fa0 | ||
|
|
0975c7d1be | ||
|
|
31836391e1 | ||
|
|
6a68347a87 | ||
|
|
c9a7019333 | ||
|
|
b6b5904a7a | ||
|
|
80fe7e010d | ||
|
|
7c36b5c4ef | ||
|
|
099799cdf5 | ||
|
|
854663ff03 | ||
|
|
885053bfbd | ||
|
|
0cfee35151 | ||
|
|
89c68b03f0 | ||
|
|
ee198dd6d2 | ||
|
|
401b512dda | ||
|
|
4fc2a6c02e | ||
|
|
7199f7ad6c | ||
|
|
8792079cb3 | ||
|
|
26d2ae36e3 | ||
|
|
2b0b90c5e9 | ||
|
|
c8ffa5203a | ||
|
|
f1ee7add1c | ||
|
|
78d7cdaf0b | ||
|
|
9028461c72 | ||
|
|
d75aec1b7e | ||
|
|
0beec63367 | ||
|
|
57871449ea | ||
|
|
038288d489 | ||
|
|
3e2fa37250 | ||
|
|
7e601d485a | ||
|
|
0bff78cd1b | ||
|
|
1fc01efe8b | ||
|
|
641b4ca13d | ||
|
|
cbb8567c76 | ||
|
|
70154b6047 | ||
|
|
d78e4e6a0b | ||
|
|
52a3761d65 | ||
|
|
07329f4304 | ||
|
|
f40585f528 | ||
|
|
9e3403ee4f | ||
|
|
76a383fb1b | ||
|
|
d4c796c73e | ||
|
|
64bd1d141a | ||
|
|
551138e7c1 | ||
|
|
7d94975960 | ||
|
|
8a7d9001fd | ||
|
|
55cae2d1f1 | ||
|
|
4fe26f24db | ||
|
|
87c440c003 | ||
|
|
ef7b07a90e | ||
|
|
17808d964d | ||
|
|
7d46ed2a85 | ||
|
|
3783eceb09 | ||
|
|
8f46cc6f09 | ||
|
|
0eb24a04ae | ||
|
|
c4ba189c05 | ||
|
|
79f277c8fe | ||
|
|
9a36dd67b2 | ||
|
|
82a98ae9d3 | ||
|
|
c994ce1dd9 | ||
|
|
1655f18a20 | ||
|
|
4c4edff9cb | ||
|
|
4997d1f63c | ||
|
|
fc900aa4b5 | ||
|
|
cae45a60b3 | ||
|
|
53e1977c65 | ||
|
|
33616c7deb | ||
|
|
25e1960511 | ||
|
|
758ae36c92 | ||
|
|
e9af74f3df | ||
|
|
c3231f1a9e | ||
|
|
fc71fd4c71 | ||
|
|
71b300aaa1 | ||
|
|
2c3ae17131 | ||
|
|
b04c3d1354 | ||
|
|
28d024af2f | ||
|
|
21f2617c9c | ||
|
|
203087bcb1 | ||
|
|
e68f54c79f |
@@ -2,9 +2,6 @@
|
||||
"presets": ["es2015"],
|
||||
"plugins": [
|
||||
"add-module-exports",
|
||||
"babel-root-import",
|
||||
["transform-react-jsx", {
|
||||
"pragma": "JSX.createElement"
|
||||
}]
|
||||
"babel-root-import"
|
||||
]
|
||||
}
|
||||
|
||||
+9
-1
@@ -18,7 +18,15 @@
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
# Build files
|
||||
# Files generated by build
|
||||
/build
|
||||
/material
|
||||
/site
|
||||
|
||||
# Files used and generated by flow
|
||||
/lib/declarations
|
||||
/tmp
|
||||
|
||||
# Files generated by visual tests
|
||||
/gemini-report
|
||||
/tests/visual/data
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
"space-unary-ops": 2,
|
||||
"spaced-comment": [2, "always", {
|
||||
"line": {
|
||||
"markers": ["/"],
|
||||
"markers": ["/", ":"],
|
||||
"exceptions": ["-", "+"]
|
||||
},
|
||||
"block": {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[ignore]
|
||||
.*/node_modules/.*
|
||||
|
||||
[libs]
|
||||
lib/declarations/
|
||||
|
||||
[options]
|
||||
strip_root=true
|
||||
@@ -25,6 +25,6 @@ CHANGED="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
|
||||
|
||||
# Perform install and prune of NPM dependencies if package.json changed
|
||||
if $(echo "$CHANGED" | grep --quiet package.json); then
|
||||
echo "Hook[post-merge]: Updating dependencies..."
|
||||
echo -e "\x1B[33m!\x1B[0m Updating dependencies"
|
||||
npm install && npm prune
|
||||
fi
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
|
||||
# Determine current branch
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
echo "Hook[pre-commit]: Checking branch..."
|
||||
MESSAGE="Commits on master are only allowed via Pull Requests. Aborting."
|
||||
|
||||
# If we're on master, abort commit
|
||||
if [[ "$BRANCH" == "master" ]]; then
|
||||
echo "Commits on master are only allowed via Pull Requests. Aborting."
|
||||
echo -e "\x1B[31m✗\x1B[0m Branch: $BRANCH - \x1B[31m$MESSAGE\x1B[0m"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\x1B[32m✓\x1B[0m Branch: $BRANCH"
|
||||
fi
|
||||
|
||||
# We're good
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
# Patch file to store unindexed changes
|
||||
PATCH_FILE=".working-tree.patch"
|
||||
MESSAGE="Terminated with errors"
|
||||
|
||||
# Revert changes that have been registered in the patch file
|
||||
function cleanup {
|
||||
@@ -30,7 +31,7 @@ function cleanup {
|
||||
git apply "$PATCH_FILE" 2> /dev/null
|
||||
rm "$PATCH_FILE"
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
exit $EXIT_CODE
|
||||
}
|
||||
|
||||
# Register signal handlers
|
||||
@@ -44,8 +45,26 @@ git checkout -- .
|
||||
FILES=$(git diff --cached --name-only --diff-filter=ACMR | \
|
||||
grep "\.\(js\|jsx\|scss\)$")
|
||||
|
||||
# Run the check and print indicator
|
||||
# Run check and print indicator
|
||||
if [ "$FILES" ]; then
|
||||
echo "Hook[pre-commit]: Running linter..."
|
||||
npm run lint --silent || exit 1
|
||||
|
||||
# If linter terminated with errors, abort commit
|
||||
if [ $? -gt 0 ]; then
|
||||
echo -e "\x1B[31m✗\x1B[0m Linter - \x1B[31m$MESSAGE\x1B[0m"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\x1B[32m✓\x1B[0m Linter"
|
||||
fi
|
||||
|
||||
# If flow terminated with errors, abort commit
|
||||
npm run flow --silent > /dev/null
|
||||
if [ $? -gt 0 ]; then
|
||||
echo -e "\x1B[31m✗\x1B[0m Flow - \x1B[31m$MESSAGE\x1B[0m"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\x1B[32m✓\x1B[0m Flow"
|
||||
fi
|
||||
fi
|
||||
|
||||
# We're good
|
||||
exit 0
|
||||
+11
-2
@@ -23,14 +23,23 @@
|
||||
|
||||
# NPM-related
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
/npm-debug.log*
|
||||
/yarn-error.log
|
||||
|
||||
# Build files
|
||||
# Files generated by build
|
||||
/build
|
||||
/manifest.json
|
||||
/MANIFEST
|
||||
/site
|
||||
|
||||
# Files generated by flow typechecker
|
||||
/tmp
|
||||
|
||||
# Files generated by visual tests
|
||||
/gemini-report
|
||||
/tests/visual/baseline/local
|
||||
/tests/visual/data
|
||||
|
||||
# Distribution files
|
||||
/dist
|
||||
/mkdocs_material.egg-info
|
||||
|
||||
@@ -180,7 +180,6 @@
|
||||
"z-index"
|
||||
],
|
||||
"property-no-vendor-prefix": true,
|
||||
"root-no-standard-properties": true,
|
||||
"selector-class-pattern": "^[a-z0-9]+(-[a-z0-9]+)*(__[a-z]+)?(--[a-z]+)?$",
|
||||
"selector-descendant-combinator-no-non-space": null,
|
||||
"string-quotes": "double",
|
||||
|
||||
+76
-3
@@ -21,21 +21,94 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Regular builds
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Node.js versions
|
||||
node_js:
|
||||
- 4
|
||||
- 5
|
||||
- 6
|
||||
- 7
|
||||
|
||||
# Limit clone depth to 5, to speed up build
|
||||
git:
|
||||
depth: 5
|
||||
|
||||
# Cache dependencies
|
||||
cache:
|
||||
pip: true
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
# Install yarn as Travis doesn't support it out of the box
|
||||
before_install:
|
||||
- npm install -g yarn
|
||||
|
||||
# Install dependencies
|
||||
before_script:
|
||||
install:
|
||||
- yarn install --ignore-optional
|
||||
- pip install --user -r requirements.txt
|
||||
|
||||
# Perform build and tests
|
||||
script: npm run build
|
||||
script:
|
||||
- yarn run build
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Additional builds
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Matrix for additional builds
|
||||
matrix:
|
||||
include:
|
||||
|
||||
# Build release and docker image and send to PyPI and Docker Hub.
|
||||
- node_js: 5
|
||||
services:
|
||||
- docker
|
||||
env:
|
||||
- __TASK=RELEASE
|
||||
|
||||
# If we're not on a release branch, exit early and indicate success
|
||||
before_install:
|
||||
- echo "$TRAVIS_BRANCH" | grep -qvE "^[0-9.]+$" && exit 0; :;
|
||||
|
||||
# Install wheel for build
|
||||
install:
|
||||
- pip install wheel
|
||||
|
||||
# Perform build
|
||||
script:
|
||||
- python setup.py build sdist bdist_wheel --universal
|
||||
- docker build -t $TRAVIS_REPO_SLUG .
|
||||
|
||||
# If build was successful, publish
|
||||
after_success:
|
||||
|
||||
# Install twine and push release to PyPI
|
||||
- pip install twine
|
||||
- twine upload -u $PYPI_USERNAME -p $PYPI_PASSWORD dist/*
|
||||
|
||||
# Push to Docker Hub
|
||||
- docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
|
||||
- docker tag $TRAVIS_REPO_SLUG $TRAVIS_REPO_SLUG:$TRAVIS_BRANCH
|
||||
- docker tag $TRAVIS_REPO_SLUG $TRAVIS_REPO_SLUG:latest
|
||||
- docker push $TRAVIS_REPO_SLUG
|
||||
|
||||
# # Build visual tests separately - temporary disabled until tests stable
|
||||
# - node_js: 5
|
||||
# addons:
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - gemini-report
|
||||
# apt:
|
||||
# sources:
|
||||
# - ubuntu-toolchain-r-test
|
||||
# packages:
|
||||
# - gcc-4.8
|
||||
# - g++-4.8
|
||||
# env:
|
||||
# - CXX=g++-4.8
|
||||
# install: yarn install
|
||||
# script: yarn run test:visual:run
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
mkdocs-material-1.1.0 (2017-02-26)
|
||||
|
||||
* Added tabs navigation feature (optional)
|
||||
* Added Disqus integration (optional)
|
||||
* Added a high resolution Favicon with the new logo
|
||||
* Added static type checking using Facebook's Flow
|
||||
* Fixed #173: Dictionary elements have no bottom spacing
|
||||
* Fixed #175: Tables cannot be set to 100% width
|
||||
* Fixed race conditions in build related to asset revisioning
|
||||
* Fixed accidentally re-introduced Permalink on top-level headline
|
||||
* Fixed alignment of logo in drawer on IE11
|
||||
* Refactored styles related to tables
|
||||
* Refactored and automated Docker build and PyPI release
|
||||
* Refactored build scripts
|
||||
|
||||
mkdocs-material-1.0.5 (2017-02-18)
|
||||
|
||||
* Fixed #153: Sidebar flows out of constrained area in Chrome 56
|
||||
* Fixed #159: Footer jitter due to JavaScript if content is short
|
||||
|
||||
mkdocs-material-1.0.4 (2017-02-16)
|
||||
|
||||
* Fixed #142: Documentation build errors if h1 is defined as raw HTML
|
||||
* Fixed #164: PyPI release does not build and install
|
||||
* Fixed offsets of targeted headlines
|
||||
* Increased sidebar font size by 0.12rem
|
||||
|
||||
mkdocs-material-1.0.3 (2017-01-22)
|
||||
|
||||
* Fixed #117: Table of contents items don't blur on fast scrolling
|
||||
|
||||
+18
-5
@@ -21,17 +21,30 @@
|
||||
FROM jfloff/alpine-python:2.7-slim
|
||||
MAINTAINER Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /docs
|
||||
# Set build directory
|
||||
WORKDIR /tmp
|
||||
|
||||
# Install packages
|
||||
# Install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN \
|
||||
pip install -r requirements.txt && \
|
||||
pip install mkdocs-material && \
|
||||
rm requirements.txt
|
||||
|
||||
# Expose MkDocs default port
|
||||
# Copy files necessary for build
|
||||
COPY material material
|
||||
COPY MANIFEST.in MANIFEST.in
|
||||
COPY package.json package.json
|
||||
COPY setup.py setup.py
|
||||
|
||||
# Perform build and cleanup artifacts
|
||||
RUN \
|
||||
python setup.py install && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /docs
|
||||
|
||||
# Expose MkDocs development server port
|
||||
EXPOSE 8000
|
||||
|
||||
# Start development server by default
|
||||
|
||||
+109
-51
@@ -35,14 +35,17 @@ const config = {
|
||||
src: "src/assets", /* Source directory for assets */
|
||||
build: "material/assets" /* Target directory for assets */
|
||||
},
|
||||
lib: "lib", /* Libraries */
|
||||
lib: "lib", /* Libraries and tasks */
|
||||
tests: {
|
||||
visual: "tests/visual" /* Base directory for visual tests */
|
||||
},
|
||||
views: {
|
||||
src: "src", /* Source directory for views */
|
||||
build: "material" /* Target directory for views */
|
||||
}
|
||||
}
|
||||
|
||||
const args = yargs
|
||||
let args = yargs
|
||||
.default("clean", false) /* Clean before build */
|
||||
.default("karma", true) /* Karma watchdog */
|
||||
.default("lint", true) /* Lint sources */
|
||||
@@ -52,6 +55,14 @@ const args = yargs
|
||||
.default("sourcemaps", false) /* Create sourcemaps */
|
||||
.argv
|
||||
|
||||
/* Only use the last seen value if boolean, so overrides are possible */
|
||||
args = Object.keys(args).reduce((result, arg) => {
|
||||
result[arg] = Array.isArray(args[arg]) && typeof args[arg][0] === "boolean"
|
||||
? [].concat(args[arg]).pop()
|
||||
: args[arg]
|
||||
return result
|
||||
}, {})
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Overrides and helpers
|
||||
* ------------------------------------------------------------------------- */
|
||||
@@ -90,9 +101,16 @@ gulp.src = (...glob) => {
|
||||
|
||||
/*
|
||||
* Helper function to load a task
|
||||
*
|
||||
* This function returns a callback that will require the task with the given
|
||||
* name and execute the function that is returned by this task. It omits the
|
||||
* need to load all tasks upfront, speeding up the build a gazillion times.
|
||||
*/
|
||||
const load = task => {
|
||||
return require(`./${config.lib}/tasks/${task}`)(gulp, config, args)
|
||||
return done => {
|
||||
return require(`./${config.lib}/tasks/${task}`)
|
||||
.call(gulp, gulp, config, args)(done)
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@@ -102,26 +120,26 @@ const load = task => {
|
||||
/*
|
||||
* Copy favicon
|
||||
*/
|
||||
gulp.task("assets:images:build:ico",
|
||||
gulp.task("assets:images:build:ico", [
|
||||
args.clean ? "assets:images:clean" : false
|
||||
].filter(t => t),
|
||||
load("assets/images/build/ico"))
|
||||
|
||||
/*
|
||||
* Copy and minify vector graphics
|
||||
*/
|
||||
gulp.task("assets:images:build:svg",
|
||||
gulp.task("assets:images:build:svg", [
|
||||
args.clean ? "assets:images:clean" : false
|
||||
].filter(t => t),
|
||||
load("assets/images/build/svg"))
|
||||
|
||||
/*
|
||||
* Copy images
|
||||
*/
|
||||
gulp.task("assets:images:build", args.clean ? [
|
||||
"assets:images:clean"
|
||||
] : [], () => {
|
||||
return gulp.start([
|
||||
"assets:images:build:ico",
|
||||
"assets:images:build:svg"
|
||||
])
|
||||
})
|
||||
gulp.task("assets:images:build", [
|
||||
"assets:images:build:ico",
|
||||
"assets:images:build:svg"
|
||||
])
|
||||
|
||||
/*
|
||||
* Clean images generated by build
|
||||
@@ -135,30 +153,39 @@ gulp.task("assets:images:clean",
|
||||
|
||||
/*
|
||||
* Build application logic
|
||||
*
|
||||
* When revisioning assets, the build must be serialized due to possible race
|
||||
* conditions when two tasks try to write manifest.json simultaneously
|
||||
*/
|
||||
gulp.task("assets:javascripts:build:application",
|
||||
|
||||
gulp.task("assets:javascripts:build:application", [
|
||||
args.clean ? "assets:javascripts:clean" : false,
|
||||
args.lint ? "assets:javascripts:lint" : false,
|
||||
args.revision ? "assets:stylesheets:build" : false
|
||||
].filter(t => t),
|
||||
load("assets/javascripts/build/application"))
|
||||
|
||||
/*
|
||||
* Build custom modernizr
|
||||
*
|
||||
* When revisioning assets, the build must be serialized due to possible race
|
||||
* conditions when two tasks try to write manifest.json simultaneously
|
||||
*/
|
||||
gulp.task("assets:javascripts:build:modernizr", [
|
||||
"assets:stylesheets:build"
|
||||
], load("assets/javascripts/build/modernizr"))
|
||||
"assets:stylesheets:build",
|
||||
args.clean ? "assets:javascripts:clean" : false,
|
||||
args.lint ? "assets:javascripts:lint" : false,
|
||||
args.revision ? "assets:javascripts:build:application" : false
|
||||
].filter(t => t),
|
||||
load("assets/javascripts/build/modernizr"))
|
||||
|
||||
/*
|
||||
* Build application logic and modernizr
|
||||
* Build application logic and Modernizr
|
||||
*/
|
||||
gulp.task("assets:javascripts:build", (args.clean ? [
|
||||
"assets:javascripts:clean"
|
||||
] : []).concat(args.lint ? [
|
||||
"assets:javascripts:lint"
|
||||
] : []), () => {
|
||||
return gulp.start([
|
||||
"assets:javascripts:build:application",
|
||||
"assets:javascripts:build:modernizr"
|
||||
])
|
||||
})
|
||||
gulp.task("assets:javascripts:build", [
|
||||
"assets:javascripts:build:application",
|
||||
"assets:javascripts:build:modernizr"
|
||||
])
|
||||
|
||||
/*
|
||||
* Clean JavaScript generated by build
|
||||
@@ -166,6 +193,12 @@ gulp.task("assets:javascripts:build", (args.clean ? [
|
||||
gulp.task("assets:javascripts:clean",
|
||||
load("assets/javascripts/clean"))
|
||||
|
||||
/*
|
||||
* Annotate JavaScript
|
||||
*/
|
||||
gulp.task("assets:javascripts:annotate",
|
||||
load("assets/javascripts/annotate"))
|
||||
|
||||
/*
|
||||
* Lint JavaScript
|
||||
*/
|
||||
@@ -179,11 +212,10 @@ gulp.task("assets:javascripts:lint",
|
||||
/*
|
||||
* Build stylesheets from SASS source
|
||||
*/
|
||||
gulp.task("assets:stylesheets:build", (args.clean ? [
|
||||
"assets:stylesheets:clean"
|
||||
] : []).concat(args.lint ? [
|
||||
"assets:stylesheets:lint"
|
||||
] : []),
|
||||
gulp.task("assets:stylesheets:build", [
|
||||
args.clean ? "assets:stylesheets:clean" : false,
|
||||
args.lint ? "assets:stylesheets:lint" : false
|
||||
].filter(t => t),
|
||||
load("assets/stylesheets/build"))
|
||||
|
||||
/*
|
||||
@@ -227,13 +259,14 @@ gulp.task("assets:clean", [
|
||||
/*
|
||||
* Minify views
|
||||
*/
|
||||
gulp.task("views:build", (args.revision ? [
|
||||
"assets:images:build",
|
||||
"assets:stylesheets:build",
|
||||
"assets:javascripts:build"
|
||||
] : []).concat(args.clean ? [
|
||||
"views:clean"
|
||||
] : []), load("views/build"))
|
||||
|
||||
gulp.task("views:build", [
|
||||
args.clean ? "views:clean" : false,
|
||||
args.revision ? "assets:images:build" : false,
|
||||
args.revision ? "assets:stylesheets:build" : false,
|
||||
args.revision ? "assets:javascripts:build" : false
|
||||
].filter(t => t),
|
||||
load("views/build"))
|
||||
|
||||
/*
|
||||
* Clean views
|
||||
@@ -267,14 +300,44 @@ gulp.task("mkdocs:serve",
|
||||
load("mkdocs/serve"))
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Tests
|
||||
* Visual tests
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Start karma test runner
|
||||
* Generate visual tests
|
||||
*/
|
||||
gulp.task("tests:unit:watch",
|
||||
load("tests/unit/watch"))
|
||||
gulp.task("tests:visual:generate", [
|
||||
args.clean ? "tests:visual:clean" : false,
|
||||
args.clean ? "assets:build" : false,
|
||||
args.clean ? "views:build" : false
|
||||
].filter(t => t),
|
||||
load("tests/visual/generate"))
|
||||
|
||||
/*
|
||||
* Run visual tests
|
||||
*/
|
||||
gulp.task("tests:visual:run", [
|
||||
"tests:visual:generate"
|
||||
], load("tests/visual/run"))
|
||||
|
||||
/*
|
||||
* Update reference images for visual tests
|
||||
*/
|
||||
gulp.task("tests:visual:update",
|
||||
load("tests/visual/update"))
|
||||
|
||||
/*
|
||||
* Clean files generated by visual tests
|
||||
*/
|
||||
gulp.task("tests:visual:clean",
|
||||
load("tests/visual/clean"))
|
||||
|
||||
/*
|
||||
* Open a SauceConnect session for manual testing
|
||||
*/
|
||||
gulp.task("tests:visual:session", [
|
||||
"tests:visual:generate"
|
||||
], load("tests/visual/session"))
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Interface
|
||||
@@ -285,10 +348,9 @@ gulp.task("tests:unit:watch",
|
||||
*/
|
||||
gulp.task("build", [
|
||||
"assets:build",
|
||||
"views:build"
|
||||
].concat(args.mkdocs
|
||||
? "mkdocs:build"
|
||||
: []))
|
||||
"views:build",
|
||||
args.mkdocs ? "mkdocs:build" : false
|
||||
].filter(f => f))
|
||||
|
||||
/*
|
||||
* Clean assets and documentation
|
||||
@@ -312,10 +374,6 @@ gulp.task("watch", [
|
||||
if (args.mkdocs)
|
||||
gulp.start("mkdocs:serve")
|
||||
|
||||
/* Start karma test runner */
|
||||
// if (args.karma)
|
||||
// gulp.start("tests:unit:watch")
|
||||
|
||||
/* Rebuild stylesheets */
|
||||
gulp.watch([
|
||||
`${config.assets.src}/stylesheets/**/*.scss`
|
||||
|
||||
@@ -3,3 +3,4 @@ recursive-exclude site *
|
||||
recursive-exclude * __pycache__
|
||||
recursive-exclude * *.py[co]
|
||||
include LICENSE
|
||||
include package.json
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
[![Travis][travis-image]][travis-link]
|
||||
[![Dependencies][deps-image]][deps-link]
|
||||
[![Codacy][codacy-image]][codacy-link]
|
||||
[![Docker][docker-image]][docker-link]
|
||||
[![PyPI][pypi-image]][pypi-link]
|
||||
|
||||
[travis-image]: https://travis-ci.org/squidfunk/mkdocs-material.svg
|
||||
[travis-image]: https://travis-ci.org/squidfunk/mkdocs-material.svg?branch=master
|
||||
[travis-link]: https://travis-ci.org/squidfunk/mkdocs-material
|
||||
[deps-image]: https://david-dm.org/squidfunk/mkdocs-material/dev-status.svg
|
||||
[deps-link]: https://david-dm.org/squidfunk/mkdocs-material?type=dev
|
||||
[codacy-image]: https://api.codacy.com/project/badge/Grade/fe07aa1fa91d453cb69711d3885c5d7e
|
||||
[codacy-link]: https://www.codacy.com/app/squidfunk/mkdocs-material?utm_source=github.com&utm_medium=referral&utm_content=squidfunk/mkdocs-material&utm_campaign=Badge_Grade
|
||||
[docker-image]: https://img.shields.io/docker/pulls/squidfunk/mkdocs-material.svg
|
||||
[docker-image]: https://img.shields.io/docker/automated/squidfunk/mkdocs-material.svg
|
||||
[docker-link]: https://hub.docker.com/r/squidfunk/mkdocs-material/
|
||||
[pypi-image]: https://img.shields.io/pypi/v/mkdocs-material.svg
|
||||
[pypi-link]: https://pypi.python.org/pypi/mkdocs-material
|
||||
|
||||
|
||||
# Material for MkDocs
|
||||
|
||||
A Material Design theme for [MkDocs](http://www.mkdocs.org).
|
||||
|
||||
+39
-22
@@ -6,7 +6,7 @@
|
||||
|
||||
The official [Docker image][1] for Material comes with all dependencies
|
||||
pre-installed and ready-to-use with the latest version published on PyPI,
|
||||
packaged in a very small image (27MB compressed).
|
||||
packaged in a very small image (28MB compressed).
|
||||
|
||||
### Installing MkDocs
|
||||
|
||||
@@ -246,7 +246,7 @@ extra:
|
||||
```
|
||||
|
||||
[12]: https://fonts.google.com/specimen/Roboto
|
||||
[13]: https://fonts.google.com/
|
||||
[13]: https://fonts.google.com
|
||||
[14]: https://fonts.google.com/specimen/Ubuntu
|
||||
|
||||
### Adding a logo
|
||||
@@ -300,6 +300,21 @@ google_analytics:
|
||||
- 'auto'
|
||||
```
|
||||
|
||||
### Disqus integation
|
||||
|
||||
Material for MkDocs is integrated with [Disqus][16], so if you want to add a
|
||||
comments section to your documentation set the shortname of your Disqus project
|
||||
in your `mkdocs.yml`:
|
||||
|
||||
``` yaml
|
||||
extra:
|
||||
disqus: 'your-disqus-shortname'
|
||||
```
|
||||
|
||||
The necessary JavaScript is automatically included.
|
||||
|
||||
[16]: https://disqus.com
|
||||
|
||||
### Localization <small>L10N</small>
|
||||
|
||||
In order to localize the labels (e.g. *Previous* and *Next* in the footer),
|
||||
@@ -309,6 +324,7 @@ translations inside the macro `t`:
|
||||
``` jinja
|
||||
{% macro t(key) %}{{ {
|
||||
"edit.link.title": "Edit this page",
|
||||
"comments": "Comments",
|
||||
"footer.previous": "Previous",
|
||||
"footer.next": "Next",
|
||||
"search.placeholder": "Search",
|
||||
@@ -318,7 +334,7 @@ translations inside the macro `t`:
|
||||
```
|
||||
|
||||
Just copy the file from the original theme and make your adjustments. See the
|
||||
section on [overriding partials][16] in the customization guide.
|
||||
section on [overriding partials][17] in the customization guide.
|
||||
|
||||
!!! warning "Migrating from Material 0.2.x"
|
||||
|
||||
@@ -326,18 +342,18 @@ section on [overriding partials][16] in the customization guide.
|
||||
`mkdocs.yml`. With 1.0.0 this is no longer possible as the configuration
|
||||
will be ignored.
|
||||
|
||||
[16]: customization.md#overriding-partials
|
||||
[17]: customization.md#overriding-partials
|
||||
|
||||
### More advanced customization
|
||||
|
||||
If you want to change the general appearance of the Material theme, see
|
||||
[this article][17] for more information on advanced customization.
|
||||
[this article][18] for more information on advanced customization.
|
||||
|
||||
[17]: customization.md
|
||||
[18]: customization.md
|
||||
|
||||
## Extensions
|
||||
|
||||
MkDocs supports several [Markdown extensions][18]. The following extensions
|
||||
MkDocs supports several [Markdown extensions][19]. The following extensions
|
||||
are not enabled by default (see the link for which are enabled by default)
|
||||
but highly recommended, so they should be switched on at all times:
|
||||
|
||||
@@ -351,18 +367,18 @@ markdown_extensions:
|
||||
For more information, see the following list of extensions supported by the
|
||||
Material theme including more information regarding installation and usage:
|
||||
|
||||
* [Admonition][19]
|
||||
* [Codehilite][20]
|
||||
* [Permalinks][21]
|
||||
* [Footnotes][22]
|
||||
* [PyMdown Extensions][23]
|
||||
* [Admonition][20]
|
||||
* [Codehilite][21]
|
||||
* [Permalinks][22]
|
||||
* [Footnotes][23]
|
||||
* [PyMdown Extensions][24]
|
||||
|
||||
[18]: http://www.mkdocs.org/user-guide/writing-your-docs/#markdown-extensions
|
||||
[19]: extensions/admonition.md
|
||||
[20]: extensions/codehilite.md
|
||||
[21]: extensions/permalinks.md
|
||||
[22]: extensions/footnotes.md
|
||||
[23]: extensions/pymdown.md
|
||||
[19]: http://www.mkdocs.org/user-guide/writing-your-docs/#markdown-extensions
|
||||
[20]: extensions/admonition.md
|
||||
[21]: extensions/codehilite.md
|
||||
[22]: extensions/permalinks.md
|
||||
[23]: extensions/footnotes.md
|
||||
[24]: extensions/pymdown.md
|
||||
|
||||
## Full example
|
||||
|
||||
@@ -380,7 +396,7 @@ repo_name: 'GitHub'
|
||||
repo_url: 'https://github.com/my-github-handle/my-project'
|
||||
|
||||
# Copyright
|
||||
copyright: 'Copyright © 2016 John Doe'
|
||||
copyright: 'Copyright © 2016 - 2017 John Doe'
|
||||
|
||||
# Documentation and theme
|
||||
theme: 'material'
|
||||
@@ -394,13 +410,14 @@ extra:
|
||||
font:
|
||||
text: 'Roboto'
|
||||
code: 'Roboto Mono'
|
||||
disqus: 'your-disqus-shortname'
|
||||
social:
|
||||
- type: 'github'
|
||||
link: 'https://github.com/squidfunk'
|
||||
link: 'https://github.com/john-doe'
|
||||
- type: 'twitter'
|
||||
link: 'https://twitter.com/squidfunk'
|
||||
link: 'https://twitter.com/jonh-doe'
|
||||
- type: 'linkedin'
|
||||
link: 'https://de.linkedin.com/in/martin-donath-20a95039'
|
||||
link: 'https://de.linkedin.com/in/john-doe'
|
||||
|
||||
# Google Analytics
|
||||
google_analytics:
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
**MIT License**
|
||||
|
||||
Copyright © 2016 Martin Donath
|
||||
Copyright © 2016 - 2017 Martin Donath
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
|
||||
+37
-1
@@ -12,11 +12,47 @@ To determine the currently installed version, use the following command:
|
||||
|
||||
``` sh
|
||||
pip show mkdocs-material | grep -E ^Version
|
||||
# Version 1.0.3
|
||||
# Version 1.1.0
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
### 1.1.0 <small> _ February 26, 2017</small>
|
||||
|
||||
* Added tabs navigation feature (optional)
|
||||
* Added Disqus integration (optional)
|
||||
* Added a high resolution Favicon with the new logo
|
||||
* Added static type checking using Facebook's Flow
|
||||
* Fixed [#173][173]: Dictionary elements have no bottom spacing
|
||||
* Fixed [#175][175]: Tables cannot be set to 100% width
|
||||
* Fixed race conditions in build related to asset revisioning
|
||||
* Fixed accidentally re-introduced Permalink on top-level headline
|
||||
* Fixed alignment of logo in drawer on IE11
|
||||
* Refactored styles related to tables
|
||||
* Refactored and automated Docker build and PyPI release
|
||||
* Refactored build scripts
|
||||
|
||||
[173]: https://github.com/squidfunk/mkdocs-material/issues/173
|
||||
[175]: https://github.com/squidfunk/mkdocs-material/issues/175
|
||||
|
||||
### 1.0.5 <small> _ February 18, 2017</small>
|
||||
|
||||
* Fixed [#153][153]: Sidebar flows out of constrained area in Chrome 56
|
||||
* Fixed [#159][159]: Footer jitter due to JavaScript if content is short
|
||||
|
||||
[153]: https://github.com/squidfunk/mkdocs-material/issues/153
|
||||
[159]: https://github.com/squidfunk/mkdocs-material/issues/159
|
||||
|
||||
### 1.0.4 <small> _ February 16, 2017</small>
|
||||
|
||||
* Fixed [#142][142]: Documentation build errors if `h1` is defined as raw HTML
|
||||
* Fixed [#164][164]: PyPI release does not build and install
|
||||
* Fixed offsets of targeted headlines
|
||||
* Increased sidebar font size by `0.12rem`
|
||||
|
||||
[142]: https://github.com/squidfunk/mkdocs-material/issues/142
|
||||
[164]: https://github.com/squidfunk/mkdocs-material/issues/164
|
||||
|
||||
### 1.0.3 <small> _ January 22, 2017</small>
|
||||
|
||||
* Fixed [#117][117]: Table of contents items don't blur on fast scrolling
|
||||
|
||||
@@ -137,6 +137,24 @@ tincidunt. Aenean ullamcorper sit amet nulla at interdum.
|
||||
sagittis. Aliquam purus tellus, faucibus eget urna at, iaculis venenatis
|
||||
nulla. Vivamus a pharetra leo.
|
||||
|
||||
### Definition lists
|
||||
|
||||
Lorem ipsum dolor sit amet
|
||||
|
||||
: Sed sagittis eleifend rutrum. Donec vitae suscipit est. Nullam tempus
|
||||
tellus non sem sollicitudin, quis rutrum leo facilisis. Nulla tempor
|
||||
lobortis orci, at elementum urna sodales vitae. In in vehicula nulla.
|
||||
|
||||
Duis mollis est eget nibh volutpat, fermentum aliquet dui mollis.
|
||||
Nam vulputate tincidunt fringilla.
|
||||
Nullam dignissim ultrices urna non auctor.
|
||||
|
||||
Cras arcu libero
|
||||
|
||||
: Aliquam metus eros, pretium sed nulla venenatis, faucibus auctor ex. Proin
|
||||
ut eros sed sapien ullamcorper consequat. Nunc ligula ante, fringilla at
|
||||
aliquam ac, aliquet sed mauris.
|
||||
|
||||
## Code blocks
|
||||
|
||||
### Inline
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-invalid-this": 0,
|
||||
"max-params": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Declarations
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
declare module "fastclick" {
|
||||
|
||||
/* Type: FastClick */
|
||||
declare type FastClick = {
|
||||
attach(name: HTMLElement): void
|
||||
}
|
||||
|
||||
/* Exports */
|
||||
declare export default FastClick
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Declarations
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
declare module "js-cookie" {
|
||||
|
||||
/* Type: Options for setting cookie values */
|
||||
declare type Options = {
|
||||
path?: string,
|
||||
expires?: number | string
|
||||
}
|
||||
|
||||
/* Type: Cookie */
|
||||
declare type Cookie = {
|
||||
getJSON(json: string): Object,
|
||||
set(key: string, value: string, options?: Options): string
|
||||
}
|
||||
|
||||
/* Exports */
|
||||
declare export default Cookie
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Declarations
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
declare class Jsx {
|
||||
static createElement(tag: string, properties?: Object,
|
||||
...children?: Array<string | number | Array<HTMLElement>>
|
||||
): HTMLElement
|
||||
}
|
||||
|
||||
/* Exports */
|
||||
declare export default Jsx
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Declarations
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Currently, it's not possible to export a function that returns a class type,
|
||||
* as the imports just don't correctly work with flow. As a workaround we
|
||||
* export an object until this error is fixed.
|
||||
*/
|
||||
declare module "lunr" {
|
||||
declare function exports(name: () => void): Object
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Declarations
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
declare class Modernizr {
|
||||
static addTest(name: string, test: () => boolean): void
|
||||
}
|
||||
|
||||
/* Exports */
|
||||
declare export default Modernizr
|
||||
@@ -21,16 +21,16 @@
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Definition
|
||||
* Module
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default /* JSX */ {
|
||||
export default /* Jsx */ {
|
||||
|
||||
/**
|
||||
* Create a native DOM node from JSX's intermediate representation
|
||||
*
|
||||
* @param {string} tag - Tag name
|
||||
* @param {object} properties - Properties
|
||||
* @param {?Object} properties - Properties
|
||||
* @param {...(string|number|Array)} children - Child nodes
|
||||
* @return {HTMLElement} Native DOM node
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import ecstatic from "ecstatic"
|
||||
import * as http from "http"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Locals
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Static file server */
|
||||
let server = null
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Start static file server
|
||||
*
|
||||
* @param {string} directory - Directory to serve
|
||||
* @param {number} port - Port to listen on
|
||||
* @param {Function} done - Resolve callback
|
||||
*/
|
||||
export const start = (directory, port, done) => {
|
||||
server = http.createServer(ecstatic({
|
||||
root: directory
|
||||
}))
|
||||
|
||||
/* Listen and register signal handlers */
|
||||
server.listen(port, "127.0.0.1", done)
|
||||
for (const signal of ["SIGTERM", "SIGINT", "exit"])
|
||||
process.on(signal, stop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop static file server
|
||||
*
|
||||
* @param {Function} done - Resolve callback
|
||||
*/
|
||||
export const stop = done => {
|
||||
if (server) {
|
||||
server.close(done)
|
||||
server = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import launcher from "sauce-connect-launcher"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Locals
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* SauceConnect process */
|
||||
let server = null
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Functions
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Open SauceConnect tunnel
|
||||
*
|
||||
* @param {string} id - Unique identifier
|
||||
* @param {string} username - SauceConnect username
|
||||
* @param {string} accesskey - SauceConnect accesskey
|
||||
* @param {Function} done - Resolve callback
|
||||
*/
|
||||
export const start = (id, username, accesskey, done) => {
|
||||
launcher({
|
||||
username,
|
||||
accessKey: accesskey,
|
||||
tunnelIdentifier: id
|
||||
}, (err, proc) => {
|
||||
if (err)
|
||||
throw new Error(err)
|
||||
server = proc
|
||||
done()
|
||||
})
|
||||
|
||||
/* Register signal handlers */
|
||||
for (const signal of ["SIGTERM", "SIGINT", "exit"])
|
||||
process.on(signal, stop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Close SauceConnect tunnel
|
||||
*
|
||||
* @param {Function} done - Resolve callback
|
||||
*/
|
||||
export const stop = done => {
|
||||
if (server) {
|
||||
server.close(done)
|
||||
server = null
|
||||
}
|
||||
}
|
||||
@@ -33,19 +33,33 @@ let server = null
|
||||
* Definition
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Start Selenium
|
||||
*
|
||||
* @param {Function} done - Resolve callback
|
||||
*/
|
||||
export const start = done => {
|
||||
selenium.start({}, (err, proc) => {
|
||||
|
||||
/* Register signal handlers */
|
||||
for (const signal of ["SIGTERM", "SIGINT", "exit"])
|
||||
process.on(signal, stop)
|
||||
if (err) {
|
||||
|
||||
/* Install selenium, if not present */
|
||||
if (/^Missing(.*)chromedriver$/.test(err.message)) {
|
||||
selenium.install(done)
|
||||
|
||||
/* Start selenium again */
|
||||
selenium.start({}, (err_, proc_) => {
|
||||
server = proc_
|
||||
new Promise(resolve => {
|
||||
selenium.install({}, resolve)
|
||||
})
|
||||
|
||||
/* Start selenium again */
|
||||
.then(() => {
|
||||
selenium.start({}, (err_, proc_) => {
|
||||
server = proc_
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
/* Otherwise, throw error */
|
||||
} else {
|
||||
throw err
|
||||
@@ -53,20 +67,21 @@ export const start = done => {
|
||||
}
|
||||
|
||||
/* Remember process handle */
|
||||
server = server || proc
|
||||
server = proc
|
||||
done()
|
||||
})
|
||||
}
|
||||
|
||||
export const stop = () => {
|
||||
if (server)
|
||||
/**
|
||||
* Stop Selenium
|
||||
*
|
||||
* @param {Function} done - Resolve callback
|
||||
*/
|
||||
export const stop = done => {
|
||||
if (server) {
|
||||
if (typeof done === "function")
|
||||
server.on("exit", done)
|
||||
server.kill()
|
||||
server = null
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Signal handler
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Register signal handler for all relevant events */
|
||||
for (const signal of ["SIGTERM", "SIGINT", "exit"])
|
||||
process.on(signal, stop)
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"rules": {
|
||||
"no-invalid-this": 0
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import changed from "gulp-changed"
|
||||
|
||||
export default (gulp, config) => {
|
||||
return () => {
|
||||
return gulp.src(`${config.assets.src}/images/**/*.ico`)
|
||||
return gulp.src(`${config.assets.src}/images/**/favicon.*`)
|
||||
.pipe(changed(`${config.assets.build}/images`))
|
||||
.pipe(gulp.dest(`${config.assets.build}/images`))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { transform } from "babel-core"
|
||||
import jsdoc2flow from "flow-jsdoc"
|
||||
import through from "through2"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Task: annotate JavaScript
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default (gulp, config) => {
|
||||
return () => {
|
||||
return gulp.src(`${config.assets.src}/javascripts/**/*.{js,jsx}`)
|
||||
|
||||
/* Linting */
|
||||
.pipe(
|
||||
through.obj(function(file, enc, done) {
|
||||
if (file.isNull() || file.isStream())
|
||||
return done()
|
||||
|
||||
/* Perform Babel transformation to resolve JSX calls */
|
||||
const transformed = transform(file.contents.toString(), {
|
||||
plugins: [
|
||||
["transform-react-jsx", {
|
||||
"pragma": "Jsx.createElement"
|
||||
}]
|
||||
]
|
||||
})
|
||||
|
||||
/* Annotate contents */
|
||||
file.contents = new Buffer(jsdoc2flow(
|
||||
`/* @flow */\n\n${transformed.code}`
|
||||
).toString())
|
||||
|
||||
/* Push file to next stage */
|
||||
this.push(file)
|
||||
done()
|
||||
}))
|
||||
|
||||
/* Print errors */
|
||||
.pipe(gulp.dest("tmp/assets/javascripts"))
|
||||
}
|
||||
}
|
||||
@@ -50,12 +50,13 @@ export default (gulp, config, args) => {
|
||||
],
|
||||
output: {
|
||||
filename: "application.js",
|
||||
library: "Application"
|
||||
library: "app",
|
||||
libraryTarget: "window"
|
||||
},
|
||||
module: {
|
||||
|
||||
/* Transpile ES6 to ES5 with Babel */
|
||||
loaders: [
|
||||
rules: [
|
||||
{
|
||||
loader: "babel-loader",
|
||||
test: /\.jsx?$/
|
||||
@@ -65,11 +66,11 @@ export default (gulp, config, args) => {
|
||||
plugins: [
|
||||
|
||||
/* Don't emit assets that include errors */
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
|
||||
/* Provide JSX helper */
|
||||
new webpack.ProvidePlugin({
|
||||
JSX: path.join(process.cwd(), `${config.lib}/providers/jsx.js`)
|
||||
Jsx: path.join(process.cwd(), `${config.lib}/providers/jsx.js`)
|
||||
})
|
||||
].concat(
|
||||
|
||||
@@ -77,19 +78,30 @@ export default (gulp, config, args) => {
|
||||
args.optimize ? [
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false
|
||||
warnings: false,
|
||||
screw_ie8: true, // eslint-disable-line camelcase
|
||||
conditionals: true,
|
||||
unused: true,
|
||||
comparisons: true,
|
||||
sequences: true,
|
||||
dead_code: true, // eslint-disable-line camelcase
|
||||
evaluate: true,
|
||||
if_return: true, // eslint-disable-line camelcase
|
||||
join_vars: true // eslint-disable-line camelcase
|
||||
},
|
||||
output: {
|
||||
comments: false
|
||||
}
|
||||
})
|
||||
] : []),
|
||||
|
||||
/* Module resolver */
|
||||
resolve: {
|
||||
modulesDirectories: [
|
||||
modules: [
|
||||
"src/assets/javascripts",
|
||||
"node_modules"
|
||||
],
|
||||
extensions: [
|
||||
"",
|
||||
".js",
|
||||
".jsx"
|
||||
]
|
||||
@@ -101,8 +113,8 @@ export default (gulp, config, args) => {
|
||||
},
|
||||
|
||||
/* Sourcemap support */
|
||||
devtool: args.sourcemaps ? "source-map" : ""
|
||||
}))
|
||||
devtool: args.sourcemaps ? "inline-source-map" : ""
|
||||
}, webpack))
|
||||
|
||||
/* Revisioning */
|
||||
.pipe(gulpif(args.revision, rev()))
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
import path from "path"
|
||||
import through from "through2"
|
||||
import util from "gulp-util"
|
||||
|
||||
import { CLIEngine } from "eslint"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@@ -38,7 +39,7 @@ const format = eslint.getFormatter()
|
||||
|
||||
export default (gulp, config) => {
|
||||
return () => {
|
||||
return gulp.src(`${config.assets.src}/javascripts/**/*.js`)
|
||||
return gulp.src(`${config.assets.src}/javascripts/**/*.{js,jsx}`)
|
||||
|
||||
/* Linting */
|
||||
.pipe(
|
||||
|
||||
@@ -25,6 +25,7 @@ import gulpif from "gulp-if"
|
||||
import mincss from "gulp-cssnano"
|
||||
import mqpacker from "css-mqpacker"
|
||||
import postcss from "gulp-postcss"
|
||||
import pseudoclasses from "postcss-pseudo-classes"
|
||||
import rev from "gulp-rev"
|
||||
import sass from "gulp-sass"
|
||||
import sourcemaps from "gulp-sourcemaps"
|
||||
@@ -54,7 +55,11 @@ export default (gulp, config, args) => {
|
||||
postcss([
|
||||
autoprefixer(),
|
||||
mqpacker
|
||||
]))
|
||||
].concat(!args.optimize ? [
|
||||
pseudoclasses({
|
||||
"restrictTo": ["hover", "focus"]
|
||||
})
|
||||
] : [])))
|
||||
|
||||
/* Minify sources */
|
||||
.pipe(gulpif(args.optimize, mincss()))
|
||||
@@ -63,7 +68,7 @@ export default (gulp, config, args) => {
|
||||
.pipe(gulpif(args.revision, rev()))
|
||||
.pipe(gulpif(args.revision,
|
||||
version({ manifest: gulp.src("manifest.json") })))
|
||||
.pipe(gulpif(args.sourcemaps, sourcemaps.write(".")))
|
||||
.pipe(gulpif(args.sourcemaps, sourcemaps.write()))
|
||||
.pipe(gulp.dest(`${config.assets.build}/stylesheets`))
|
||||
.pipe(gulpif(args.revision,
|
||||
rev.manifest("manifest.json", {
|
||||
|
||||
@@ -39,7 +39,7 @@ export default () => {
|
||||
server.kill()
|
||||
|
||||
/* Spawn MkDocs server */
|
||||
server = child.spawn("mkdocs", ["serve", "-a", "0.0.0.0:8000"], {
|
||||
server = child.spawn("mkdocs", ["serve", "--dev-addr", "0.0.0.0:8000"], {
|
||||
stdio: "inherit"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import clean from "del"
|
||||
import vinyl from "vinyl-paths"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Task: clean files generated by visual tests
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default (gulp, config) => {
|
||||
return () => {
|
||||
return gulp.src([
|
||||
`${config.tests.visual}/data`,
|
||||
"./gemini-report"
|
||||
])
|
||||
.pipe(vinyl(clean))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import child from "child_process"
|
||||
import path from "path"
|
||||
import through from "through2"
|
||||
import util from "gulp-util"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Task: generate visual tests
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default (gulp, config) => {
|
||||
const theme = path.resolve(process.cwd(), config.views.build)
|
||||
return () => {
|
||||
return gulp.src(`${config.tests.visual}/suites/**/mkdocs.yml`)
|
||||
.pipe(
|
||||
through.obj(function(file, enc, done) {
|
||||
if (file.isNull() || file.isStream())
|
||||
return done()
|
||||
|
||||
/* Resolve test name and destination */
|
||||
const name = path.relative(`${config.tests.visual}/suites`,
|
||||
path.dirname(file.path))
|
||||
const site = path.resolve(process.cwd(),
|
||||
`${config.tests.visual}/data`, name, "_")
|
||||
|
||||
/* Generate test fixtures with freshly built theme */
|
||||
const proc = child.spawnSync("mkdocs", [
|
||||
"build", "--site-dir", site, "--theme-dir", theme
|
||||
], {
|
||||
cwd: path.dirname(file.path)
|
||||
})
|
||||
|
||||
/* Emit error, if any */
|
||||
if (proc.status)
|
||||
this.emit("error", new util.PluginError("mkdocs",
|
||||
`Terminated with errors: ${proc.stderr.toString()}`))
|
||||
|
||||
/* Terminate */
|
||||
done()
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import moniker from "moniker"
|
||||
import path from "path"
|
||||
import * as ecstatic from "~/lib/servers/ecstatic"
|
||||
import * as sauce from "~/lib/servers/sauce-connect"
|
||||
import * as selenium from "~/lib/servers/selenium"
|
||||
|
||||
import Gemini from "gemini"
|
||||
import SauceLabs from "saucelabs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Locals
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* SauceLabs job name */
|
||||
const id = process.env.TRAVIS
|
||||
? `Travis #${process.env.TRAVIS_BUILD_NUMBER}`
|
||||
: `Local #${moniker.choose()}`
|
||||
|
||||
/* SauceLabs test results */
|
||||
const passed = {}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Task: run visual tests
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default (gulp, config, args) => {
|
||||
return done => {
|
||||
|
||||
/* Start static file server */
|
||||
let error = false
|
||||
new Promise(resolve => {
|
||||
ecstatic.start(`${config.tests.visual}/data`, 8000, resolve)
|
||||
|
||||
/* Create and start test runner */
|
||||
}).then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
/* Start SauceConnect tunnel */
|
||||
if (process.env.CI || process.env.SAUCE) {
|
||||
if (!process.env.SAUCE_USERNAME ||
|
||||
!process.env.SAUCE_ACCESS_KEY)
|
||||
throw new Error(
|
||||
"SauceConnect: please provide SAUCE_USERNAME " +
|
||||
"and SAUCE_ACCESS_KEY")
|
||||
|
||||
/* Start tunnel, if credentials are given */
|
||||
sauce.start(
|
||||
id,
|
||||
process.env.SAUCE_USERNAME,
|
||||
process.env.SAUCE_ACCESS_KEY,
|
||||
err => {
|
||||
return err ? reject(err) : resolve(sauce)
|
||||
})
|
||||
|
||||
/* Start Selenium */
|
||||
} else {
|
||||
selenium.start(() => resolve(selenium))
|
||||
}
|
||||
})
|
||||
|
||||
/* Setup and run Gemini */
|
||||
.then(runner => {
|
||||
const setup = require(
|
||||
path.join(process.cwd(), `${config.tests.visual}/config`,
|
||||
process.env.CI || process.env.SAUCE
|
||||
? "gemini.sauce-connect.json"
|
||||
: "gemini.selenium.json"))
|
||||
|
||||
/* Add dynamic configuration to capabilities */
|
||||
for (const key of Object.keys(setup.browsers)) {
|
||||
const caps = setup.browsers[key].desiredCapabilities
|
||||
caps.tunnelIdentifier = id
|
||||
caps.public = "private"
|
||||
caps.name = id
|
||||
|
||||
/* Adjust configuration for Travis CI */
|
||||
if (process.env.CI && process.env.TRAVIS)
|
||||
caps.public = "public"
|
||||
}
|
||||
|
||||
/* Setup Gemini and test listeners */
|
||||
const gemini = new Gemini(setup)
|
||||
if (process.env.CI || process.env.SAUCE) {
|
||||
|
||||
/* Initialize test run */
|
||||
gemini.on(gemini.events.START_BROWSER, job => {
|
||||
passed[job.sessionId] = true
|
||||
})
|
||||
|
||||
/* Update state of test run */
|
||||
gemini.on(gemini.events.TEST_RESULT, job => {
|
||||
passed[job.sessionId] = passed[job.sessionId] && job.equal
|
||||
})
|
||||
}
|
||||
|
||||
/* Run tests */
|
||||
return gemini.test(`${config.tests.visual}/suites`, {
|
||||
reporters: ["flat", "html"],
|
||||
browsers: args.browsers ? [].concat(args.browsers) : null
|
||||
})
|
||||
|
||||
/* Return runner for graceful stop */
|
||||
.then(status => {
|
||||
error = status.failed + status.errored > 0
|
||||
return runner
|
||||
})
|
||||
})
|
||||
|
||||
/* Stop test runner */
|
||||
.then(runner => {
|
||||
return new Promise(resolve => {
|
||||
runner.stop(resolve)
|
||||
})
|
||||
})
|
||||
|
||||
/* Update SauceLabs jobs with test results */
|
||||
.then(() => {
|
||||
const saucelabs = new SauceLabs({
|
||||
username: process.env.SAUCE_USERNAME,
|
||||
password: process.env.SAUCE_ACCESS_KEY
|
||||
})
|
||||
const updates = Object.keys(passed).map(sessionId => {
|
||||
return new Promise(resolve => {
|
||||
saucelabs.updateJob(sessionId, {
|
||||
passed: passed[sessionId]
|
||||
}, resolve)
|
||||
})
|
||||
})
|
||||
return Promise.all(updates)
|
||||
})
|
||||
|
||||
/* Stop static file server */
|
||||
})
|
||||
.then(() => {
|
||||
ecstatic.stop(() => {
|
||||
return error
|
||||
? done(new Error("Gemini terminated with errors"))
|
||||
: done()
|
||||
})
|
||||
}, err => {
|
||||
return done(new Error(err))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import moniker from "moniker"
|
||||
import * as ecstatic from "~/lib/servers/ecstatic"
|
||||
import * as sauce from "~/lib/servers/sauce-connect"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Task: run visual tests
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default (gulp, config) => {
|
||||
return done => {
|
||||
|
||||
/* Start static file server */
|
||||
new Promise(resolve => {
|
||||
ecstatic.start(`${config.tests.visual}/data`, 8000, resolve)
|
||||
|
||||
/* Open SauceConnect tunnel */
|
||||
}).then(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
/* Start SauceConnect tunnel */
|
||||
if (process.env.CI || process.env.SAUCE) {
|
||||
if (!process.env.SAUCE_USERNAME ||
|
||||
!process.env.SAUCE_ACCESS_KEY)
|
||||
throw new Error(
|
||||
"SauceConnect: please provide SAUCE_USERNAME " +
|
||||
"and SAUCE_ACCESS_KEY")
|
||||
|
||||
/* Open tunnel */
|
||||
sauce.start(
|
||||
`Local #${moniker.choose()}`,
|
||||
process.env.SAUCE_USERNAME,
|
||||
process.env.SAUCE_ACCESS_KEY,
|
||||
err => {
|
||||
return err ? reject(err) : resolve(sauce)
|
||||
})
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
/* Close tunnel on CTRL-C */
|
||||
.then(runner => {
|
||||
return new Promise(resolve => {
|
||||
process.on("SIGINT", () => {
|
||||
return runner
|
||||
? runner.stop(resolve)
|
||||
: resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
/* Stop static file server */
|
||||
})
|
||||
.then(() => {
|
||||
ecstatic.stop(done)
|
||||
}, err => {
|
||||
return done(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
import through from "through2"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Task: update reference images for visual tests
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default (gulp, config) => {
|
||||
return () => {
|
||||
const base = path.join(
|
||||
process.cwd(), `${config.tests.visual}/config`)
|
||||
|
||||
/* Read Gemini configs and map browsers to screenshot directories */
|
||||
const mapping = fs.readdirSync(base)
|
||||
.reduce((result, filename) => {
|
||||
return Object.assign(result, (gemini => {
|
||||
return Object.keys(gemini.browsers)
|
||||
.reduce((browsers, name) => {
|
||||
browsers[name] = gemini.screenshotsDir
|
||||
return browsers
|
||||
}, {})
|
||||
})(require(path.join(base, filename))))
|
||||
}, {})
|
||||
|
||||
/* Prepare filenames */
|
||||
const dest = path.join(process.cwd(), `${config.tests.visual}/baseline`)
|
||||
return gulp.src("gemini-report/images/**/*~current.png")
|
||||
.pipe(
|
||||
through.obj(function(file, enc, done) {
|
||||
if (file.isNull() || file.isStream())
|
||||
return done()
|
||||
|
||||
/* Remove the state from the filename */
|
||||
file.path = file.path.replace("~current", "")
|
||||
|
||||
/* Retrieve the folder for the environment of the baseline */
|
||||
const folder = path.relative(dest,
|
||||
mapping[path.basename(file.path, ".png")])
|
||||
file.path = file.path.replace("images", `images/${folder}`)
|
||||
|
||||
/* Push file to next stage */
|
||||
this.push(file)
|
||||
done()
|
||||
}))
|
||||
|
||||
/* Update reference images */
|
||||
.pipe(gulp.dest(dest))
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import vinyl from "vinyl-paths"
|
||||
|
||||
export default (gulp, config) => {
|
||||
return () => {
|
||||
return gulp.src(`${config.views.build}/**/*.html`)
|
||||
return gulp.src(`${config.views.build}/**/*.{html,py}`)
|
||||
.pipe(vinyl(clean))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
{% extends "main.html" %}
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>404 - Not found</h1>
|
||||
{% endblock %}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+37
-18
@@ -17,21 +17,25 @@
|
||||
{% if config.site_favicon %}
|
||||
<link rel="shortcut icon" href="{{ base_url }}/{{ config.site_favicon }}">
|
||||
{% else %}
|
||||
<link rel="shortcut icon" href="{{ base_url }}/assets/images/favicon.ico">
|
||||
<link rel="shortcut icon" href="{{ base_url }}/assets/images/favicon.png">
|
||||
{% endif %}
|
||||
<meta name="generator" content="mkdocs+mkdocs-material#1.0.3">
|
||||
<meta name="generator" content="mkdocs-{{ mkdocs_version }}, mkdocs-material-1.1.0">
|
||||
{% endblock %}
|
||||
{% block htmltitle %}
|
||||
{% if page.title %}
|
||||
{% if page.title and not page.is_homepage %}
|
||||
<title>{{ page.title }} - {{ config.site_name }}</title>
|
||||
{% elif config.site_description %}
|
||||
<title>{{ config.site_name }} - {{ config.site_description }}</title>
|
||||
{% else %}
|
||||
<title>{{ config.site_name }}</title>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block libs %}
|
||||
<script src="{{ base_url }}/assets/javascripts/modernizr-facb31f4a3.js"></script>
|
||||
<script src="{{ base_url }}/assets/javascripts/modernizr-56ade86843.js"></script>
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-45f91e8d31.css">
|
||||
{% if config.extra.palette %}
|
||||
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-49c4a440b6.palette.css">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block fonts %}
|
||||
{% if config.extra.font != "none" %}
|
||||
@@ -44,15 +48,9 @@
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
{% endblock %}
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-f3ab63f78a.css">
|
||||
{% if config.extra.palette %}
|
||||
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-02ce7adcc2.palette.css">
|
||||
{% endif %}
|
||||
{% for path in extra_css %}
|
||||
<link rel="stylesheet" href="{{ path }}">
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
{% for path in extra_css %}
|
||||
<link rel="stylesheet" href="{{ path }}">
|
||||
{% endfor %}
|
||||
{% block extrahead %}{% endblock %}
|
||||
</head>
|
||||
{% set palette = config.extra.get("palette", {}) %}
|
||||
@@ -82,6 +80,10 @@
|
||||
{% include "partials/header.html" %}
|
||||
{% endblock %}
|
||||
<div class="md-container">
|
||||
{% set feature = config.extra.get("feature", {}) %}
|
||||
{% if feature.tabs %}
|
||||
{% include "partials/tabs.html" %}
|
||||
{% endif %}
|
||||
<main class="md-main">
|
||||
<div class="md-main__inner md-grid" data-md-component="container">
|
||||
{% block site_nav %}
|
||||
@@ -110,11 +112,28 @@
|
||||
<a href="{{ page.edit_url }}" title="{{ lang.t('edit.link.title') }}" class="md-icon md-content__edit">edit</a>
|
||||
{% endif %}
|
||||
{% block content %}
|
||||
{% if not "\x3ch1 id=" in page.content %}
|
||||
{% if not "\x3ch1" in page.content %}
|
||||
<h1>{{ page.title | default(config.site_name, true)}}</h1>
|
||||
{% endif %}
|
||||
{{ page.content }}
|
||||
{% endblock %}
|
||||
{% if config.extra.disqus and not page.is_homepage %}
|
||||
<h2>{{ lang.t('comments') }}</h2>
|
||||
<div id="disqus_thread"></div>
|
||||
<script>
|
||||
var disqus_config = function () {
|
||||
this.page.url = "{{ page.canonical_url }}";
|
||||
this.page.identifier =
|
||||
"{{ page.canonical_url | replace(config.site_url, "") }}";
|
||||
};
|
||||
(function() {
|
||||
var d = document, s = d.createElement("script");
|
||||
s.src = "//{{ config.extra.disqus }}.disqus.com/embed.js";
|
||||
s.setAttribute("data-timestamp", +new Date());
|
||||
(d.head || d.body).appendChild(s);
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,8 +143,8 @@
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% block scripts %}
|
||||
<script src="{{ base_url }}/assets/javascripts/application-2afe21e0b2.js"></script>
|
||||
<script>var config={url:{base:"{{ base_url }}"}},app=new Application(config);app.initialize()</script>
|
||||
<script src="{{ base_url }}/assets/javascripts/application-30ac6a1727.js"></script>
|
||||
<script>app.initialize({url:{base:"{{ base_url }}"}})</script>
|
||||
{% for path in extra_javascript %}
|
||||
<script src="{{ path }}"></script>
|
||||
{% endfor %}
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
<nav class="md-header-nav md-grid">
|
||||
<div class="md-flex">
|
||||
<div class="md-flex__cell md-flex__cell--shrink">
|
||||
<a href="{{ nav.homepage.url }}" title="{{ config.site_name }}" class="{% if config.extra.logo %} md-logo {% else %} md-icon md-icon--home {% endif %} md-header-nav__button">
|
||||
{% if config.extra.logo %}
|
||||
{% if config.extra.logo %}
|
||||
<a href="{{ nav.homepage.url }}" title="{{ config.site_name }}" class="md-logo md-header-nav__button">
|
||||
<img src="{{ base_url }}/{{ config.extra.logo }}" width="24" height="24">
|
||||
{% endif %}
|
||||
</a>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ nav.homepage.url }}" title="{{ config.site_name }}" class="md-icon md-icon--home md-header-nav__button">
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="md-flex__cell md-flex__cell--shrink">
|
||||
<label class="md-icon md-icon--menu md-header-nav__button" for="drawer"></label>
|
||||
@@ -32,11 +35,11 @@
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="md-flex__cell md-flex__cell--shrink">
|
||||
<div class="md-header-nav__source">
|
||||
{% if config.repo_url %}
|
||||
{% include "partials/source.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="md-header-nav__source">
|
||||
{% if config.repo_url %}
|
||||
{% include "partials/source.html" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% macro t(key) %}{{ {
|
||||
"edit.link.title": "Edit this page",
|
||||
"comments": "Comments",
|
||||
"footer.previous": "Previous",
|
||||
"footer.next": "Next",
|
||||
"search.placeholder": "Search",
|
||||
|
||||
@@ -8,14 +8,15 @@
|
||||
<label class="md-nav__link" for="{{ path }}">
|
||||
{{ nav_item.title }}
|
||||
</label>
|
||||
<nav class="md-nav" data-md-component="collapsible">
|
||||
<nav class="md-nav" data-md-component="collapsible" data-md-level="{{ level }}">
|
||||
<label class="md-nav__title" for="{{ path }}">
|
||||
{{ nav_item.title}}
|
||||
{{ nav_item.title }}
|
||||
</label>
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
{% set base = path %}
|
||||
{% for nav_item in nav_item.children %}
|
||||
{% set path = base + "-" + loop.index | string %}
|
||||
{% set level = level + 1 %}
|
||||
{% include "partials/nav-item.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
@@ -25,10 +26,10 @@
|
||||
<li class="md-nav__item">
|
||||
{% set toc_ = page.toc %}
|
||||
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc">
|
||||
{% if "\x3ch1 id=" in page.content %}
|
||||
{% if toc_ | first is defined %}
|
||||
{% set toc_ = (toc_ | first).children %}
|
||||
{% endif %}
|
||||
{% if toc_ and (toc_ | first) %}
|
||||
{% if toc_ | first is defined %}
|
||||
<label class="md-nav__link md-nav__link--active" for="toc">
|
||||
{{ nav_item.title }}
|
||||
</label>
|
||||
@@ -36,7 +37,7 @@
|
||||
<a href="{{ nav_item.url }}" title="{{ nav_item.title }}" class="md-nav__link md-nav__link--active">
|
||||
{{ nav_item.title }}
|
||||
</a>
|
||||
{% if page.toc %}
|
||||
{% if toc_ | first is defined %}
|
||||
{% include "partials/toc.html" %}
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<nav class="md-nav md-nav--primary">
|
||||
<nav class="md-nav md-nav--primary" data-md-level="0">
|
||||
<label class="md-nav__title md-nav__title--site" for="drawer">
|
||||
<i class="{% if config.extra.logo %} md-logo {% else %} md-icon md-icon--home {% endif %} md-nav__button">
|
||||
{% if config.extra.logo %}
|
||||
{% if config.extra.logo %}
|
||||
<i class="md-logo md-nav__button">
|
||||
<img src="{{ base_url }}/{{ config.extra.logo }}">
|
||||
{% endif %}
|
||||
</i>
|
||||
</i>
|
||||
{% else %}
|
||||
<i class="md-icon md-icon--home md-nav__button"></i>
|
||||
{% endif %}
|
||||
{{ config.site_name }}
|
||||
</label>
|
||||
{% if config.repo_url %}
|
||||
@@ -15,6 +17,7 @@
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
{% for nav_item in nav %}
|
||||
{% set path = "nav-" + loop.index | string %}
|
||||
{% set level = 1 %}
|
||||
{% include "partials/nav-item.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="md-search__overlay"></div>
|
||||
<div class="md-search__inner">
|
||||
<form class="md-search__form" name="search">
|
||||
<input type="text" class="md-search__input" name="query" placeholder="{{ lang.t('search.placeholder') }}" accesskey="s" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false">
|
||||
<input type="text" class="md-search__input" name="query" placeholder="{{ lang.t('search.placeholder') }}" accesskey="s" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query">
|
||||
<label class="md-icon md-search__icon" for="search"></label>
|
||||
</form>
|
||||
<div class="md-search__output">
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% if nav_item.is_homepage %}
|
||||
<li class="md-tabs__item">
|
||||
{% if not page.ancestors | length and nav | selectattr("url", page.url) %}
|
||||
<a href="{{ nav_item.url }}" title="{{ nav_item.title }}" class="md-tabs__link md-tabs__link--active">
|
||||
{{ nav_item.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ nav_item.url }}" title="{{ nav_item.title }}" class="md-tabs__link">
|
||||
{{ nav_item.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% elif nav_item.children and nav_item.children | length > 0 %}
|
||||
<li class="md-tabs__item">
|
||||
{% if nav_item.active %}
|
||||
<a href="{{ (nav_item.children | first).url }}" title="{{ nav_item.title }}" class="md-tabs__link md-tabs__link--active">
|
||||
{{ nav_item.title }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ (nav_item.children | first).url }}" title="{{ nav_item.title }}" class="md-tabs__link">
|
||||
{{ nav_item.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,13 @@
|
||||
{% set class = "md-tabs" %}
|
||||
{% if page.ancestors | length > 0 %}
|
||||
{% set class = "md-tabs md-tabs--active" %}
|
||||
{% endif %}
|
||||
<nav class="{{ class }}" data-md-component="tabs">
|
||||
<div class="md-tabs__inner md-grid">
|
||||
<ul class="md-tabs__list">
|
||||
{% for nav_item in nav %}
|
||||
{% include "partials/tabs-item.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1,10 +1,10 @@
|
||||
{% import "partials/language.html" as lang %}
|
||||
<nav class="md-nav md-nav--secondary">
|
||||
{% set toc_ = page.toc %}
|
||||
{% if "\x3ch1 id=" in page.content %}
|
||||
{% if toc_ | first is defined and "\x3ch1 id=" in page.content %}
|
||||
{% set toc_ = (toc_ | first).children %}
|
||||
{% endif %}
|
||||
{% if toc_ and (toc_ | first) %}
|
||||
{% if toc_ | first is defined %}
|
||||
<label class="md-nav__title" for="toc">{{ lang.t('toc.title') }}</label>
|
||||
<ul class="md-nav__list" data-md-scrollfix>
|
||||
{% for toc_item in toc_ %}
|
||||
|
||||
+2
-1
@@ -31,7 +31,7 @@ repo_url: https://github.com/squidfunk/mkdocs-material
|
||||
# Copyright
|
||||
copyright: 'Copyright © 2016 - 2017 Martin Donath'
|
||||
|
||||
# Documentation and theme
|
||||
# Theme directory
|
||||
theme_dir: material
|
||||
|
||||
# Options
|
||||
@@ -51,6 +51,7 @@ extra:
|
||||
markdown_extensions:
|
||||
- markdown.extensions.admonition
|
||||
- markdown.extensions.codehilite(guess_lang=false)
|
||||
- markdown.extensions.def_list
|
||||
- markdown.extensions.footnotes
|
||||
- markdown.extensions.meta
|
||||
- markdown.extensions.toc(permalink=true)
|
||||
|
||||
+35
-29
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mkdocs-material",
|
||||
"version": "1.0.3",
|
||||
"version": "1.1.0",
|
||||
"description": "A Material Design theme for MkDocs",
|
||||
"keywords": [
|
||||
"mkdocs",
|
||||
@@ -25,32 +25,37 @@
|
||||
"scripts": {
|
||||
"build": "scripts/build",
|
||||
"clean": "scripts/clean",
|
||||
"flow": "scripts/flow",
|
||||
"lint": "scripts/lint",
|
||||
"start": "scripts/start",
|
||||
"test": "scripts/test"
|
||||
"test:visual:run": "scripts/test/visual/run",
|
||||
"test:visual:update": "scripts/test/visual/update",
|
||||
"test:visual:session": "scripts/test/visual/session"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.6.1",
|
||||
"autoprefixer": "^6.7.3",
|
||||
"babel-core": "^6.23.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.2.10",
|
||||
"babel-loader": "^6.3.1",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-transform-react-jsx": "^6.8.0",
|
||||
"babel-polyfill": "^6.20.0",
|
||||
"babel-preset-es2015": "^6.22.0",
|
||||
"babel-register": "^6.18.0",
|
||||
"babel-register": "^6.23.0",
|
||||
"babel-root-import": "^4.1.5",
|
||||
"chai": "^3.5.0",
|
||||
"core-js": "^2.4.1",
|
||||
"css-mqpacker": "^5.0.1",
|
||||
"custom-event-polyfill": "^0.3.0",
|
||||
"del": "^2.2.2",
|
||||
"eslint": "^3.14.0",
|
||||
"eslint-plugin-mocha": "^4.8.0",
|
||||
"ecstatic": "^2.1.0",
|
||||
"eslint": "^3.16.0",
|
||||
"fastclick": "^1.0.6",
|
||||
"flow-bin": "^0.39.0",
|
||||
"flow-jsdoc": "^0.2.2",
|
||||
"git-hooks": "^1.1.7",
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-changed": "^1.3.2",
|
||||
"gulp-changed": "^2.0.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-cssnano": "^2.1.2",
|
||||
"gulp-htmlmin": "^3.0.0",
|
||||
@@ -70,34 +75,35 @@
|
||||
"gulp-uglify": "^2.0.0",
|
||||
"gulp-util": "^3.0.8",
|
||||
"js-cookie": "^2.1.3",
|
||||
"karma": "^1.3.0",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-notify-reporter": "^1.0.1",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "0.0.26",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"lunr": "^0.7.2",
|
||||
"material-design-color": "^2.3.2",
|
||||
"material-shadows": "^3.0.1",
|
||||
"mocha": "^3.2.0",
|
||||
"modularscale-sass": "^2.1.1",
|
||||
"node-notifier": "^4.6.1",
|
||||
"selenium-standalone": "^5.9.1",
|
||||
"stylelint": "^7.7.1",
|
||||
"stylelint-config-standard": "^15.0.1",
|
||||
"stylelint-order": "^0.2.2",
|
||||
"lunr": "^1.0.0",
|
||||
"material-design-color": "2.3.2",
|
||||
"material-shadows": "3.0.1",
|
||||
"modularscale-sass": "2.1.1",
|
||||
"node-notifier": "^5.0.0",
|
||||
"postcss-pseudo-classes": "^0.2.0",
|
||||
"stylelint": "^7.8.0",
|
||||
"stylelint-config-standard": "^16.0.0",
|
||||
"stylelint-order": "^0.3.0",
|
||||
"stylelint-scss": "^1.4.1",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl-paths": "^2.1.0",
|
||||
"webpack": "^1.14.0",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-stream": "^3.2.0",
|
||||
"whatwg-fetch": "^2.0.1",
|
||||
"yargs": "^6.6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"chai": "^3.5.0",
|
||||
"eslint-plugin-mocha": "^4.8.0",
|
||||
"gemini": "^4.14.3",
|
||||
"mocha": "^3.2.0",
|
||||
"moniker": "^0.1.2",
|
||||
"saucelabs": "^1.4.0",
|
||||
"sauce-connect-launcher": "^1.2.0",
|
||||
"selenium-standalone": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.5.0"
|
||||
"node": ">= 5.0.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,4 +20,4 @@
|
||||
|
||||
mkdocs>=0.16
|
||||
pygments
|
||||
pymdown-extensions
|
||||
pymdown-extensions>=1.2
|
||||
|
||||
+1
-1
@@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
|
||||
fi
|
||||
|
||||
# Run command
|
||||
`npm bin`/gulp build --clean --optimize --revision
|
||||
`npm bin`/gulp build --clean --optimize --revision "$@"
|
||||
|
||||
+1
-1
@@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
|
||||
fi
|
||||
|
||||
# Run command
|
||||
`npm bin`/gulp clean
|
||||
`npm bin`/gulp clean "$@"
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
# Check if "npm install" was executed
|
||||
if [[ ! -d `npm bin` ]]; then
|
||||
echo "\"node_modules\" not found:"
|
||||
echo "npm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Annotate source files
|
||||
`npm bin`/gulp assets:javascripts:annotate "$@"
|
||||
FLOW_JSDOC=$?
|
||||
|
||||
# Run flow typecheck
|
||||
`npm bin`/flow check tmp
|
||||
FLOW=$?
|
||||
|
||||
# If one command failed, exit with error
|
||||
if [ $FLOW_JSDOC -gt 0 ] || [ $FLOW -gt 0 ]; then
|
||||
exit 1
|
||||
fi;
|
||||
|
||||
# Otherwise return with success
|
||||
exit 0
|
||||
+1
-1
@@ -28,7 +28,7 @@ if [[ ! -d `npm bin` ]]; then
|
||||
fi
|
||||
|
||||
# Run ESLint
|
||||
`npm bin`/eslint .
|
||||
`npm bin`/eslint --max-warnings 0 .
|
||||
ESLINT=$?
|
||||
|
||||
# Run Stylelint
|
||||
|
||||
+2
-1
@@ -28,4 +28,5 @@ if [[ ! -d `npm bin` ]]; then
|
||||
fi
|
||||
|
||||
# Run command
|
||||
`npm bin`/gulp watch --no-lint
|
||||
`npm bin`/gulp clean && \
|
||||
`npm bin`/gulp watch --no-lint "$@"
|
||||
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
# Check if "npm install" was executed
|
||||
if [[ ! -d `npm bin` ]]; then
|
||||
echo "\"node_modules\" not found:"
|
||||
echo "npm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run command
|
||||
`npm bin`/gulp tests:visual:run --clean --no-optimize "$@"
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
# Check if "npm install" was executed
|
||||
if [[ ! -d `npm bin` ]]; then
|
||||
echo "\"node_modules\" not found:"
|
||||
echo "npm install"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run command
|
||||
`npm bin`/gulp tests:visual:session "$@"
|
||||
@@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
|
||||
fi
|
||||
|
||||
# Run command
|
||||
`npm bin`/gulp test
|
||||
`npm bin`/gulp tests:visual:update "$@"
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"presets": [
|
||||
["es2015", { "modules": false }]
|
||||
],
|
||||
"plugins": [
|
||||
["transform-react-jsx", {
|
||||
"pragma": "Jsx.createElement"
|
||||
}]
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
IN THE SOFTWARE.
|
||||
-->
|
||||
|
||||
{% extends "main.html" %}
|
||||
{% extends "base.html" %}
|
||||
|
||||
<!-- Content block -->
|
||||
{% block content %}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
@@ -27,215 +27,241 @@ import Material from "./components/Material"
|
||||
* Application
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default class Application {
|
||||
/**
|
||||
* Initialize Material for MkDocs
|
||||
*
|
||||
* @param {Object} config - Configuration
|
||||
*/
|
||||
function initialize(config) { // eslint-disable-line func-style
|
||||
|
||||
/**
|
||||
* Create the application
|
||||
*
|
||||
* @constructor
|
||||
* @param {object} config Configuration object
|
||||
*/
|
||||
constructor(config) {
|
||||
this.config_ = config
|
||||
}
|
||||
/* Initialize Modernizr and FastClick */
|
||||
new Material.Event.Listener(document, "DOMContentLoaded", () => {
|
||||
if (!(document.body instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/**
|
||||
* Initialize all components and listeners
|
||||
*/
|
||||
initialize() {
|
||||
/* Attach FastClick to mitigate 300ms delay on touch devices */
|
||||
FastClick.attach(document.body)
|
||||
|
||||
/* Initialize Modernizr and FastClick */
|
||||
new Material.Event.Listener(document, "DOMContentLoaded", () => {
|
||||
/* Test for iOS */
|
||||
Modernizr.addTest("ios", () => {
|
||||
return !!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)
|
||||
})
|
||||
|
||||
/* Test for iOS */
|
||||
Modernizr.addTest("ios", () => {
|
||||
return !!navigator.userAgent.match(/(iPad|iPhone|iPod)/g)
|
||||
})
|
||||
/* Wrap all data tables for better overflow scrolling */
|
||||
const tables = document.querySelectorAll("table:not([class])")
|
||||
Array.prototype.forEach.call(tables, table => {
|
||||
const wrap = (
|
||||
<div class="md-typeset__scrollwrap">
|
||||
<div class="md-typeset__table"></div>
|
||||
</div>
|
||||
)
|
||||
if (table.nextSibling) {
|
||||
table.parentNode.insertBefore(wrap, table.nextSibling)
|
||||
} else {
|
||||
table.parentNode.appendChild(wrap)
|
||||
}
|
||||
wrap.children[0].appendChild(table)
|
||||
})
|
||||
|
||||
/* Test for web application context */
|
||||
Modernizr.addTest("standalone", () => {
|
||||
return !!navigator.standalone
|
||||
})
|
||||
/* Force 1px scroll offset to trigger overflow scrolling */
|
||||
if (Modernizr.ios) {
|
||||
const scrollable = document.querySelectorAll("[data-md-scrollfix]")
|
||||
Array.prototype.forEach.call(scrollable, item => {
|
||||
item.addEventListener("touchstart", () => {
|
||||
const top = item.scrollTop
|
||||
|
||||
/* Attack FastClick to mitigate 300ms delay on touch devices */
|
||||
FastClick.attach(document.body)
|
||||
|
||||
/* Wrap all data tables for better overflow scrolling */
|
||||
const tables = document.querySelectorAll("table:not([class])")
|
||||
Array.prototype.forEach.call(tables, table => {
|
||||
const wrap = document.createElement("div")
|
||||
wrap.classList.add("md-typeset__table")
|
||||
if (table.nextSibling) {
|
||||
table.parentNode.insertBefore(wrap, table.nextSibling)
|
||||
} else {
|
||||
table.parentNode.appendChild(wrap)
|
||||
}
|
||||
wrap.appendChild(table)
|
||||
})
|
||||
|
||||
/* Force 1px scroll offset to trigger overflow scrolling */
|
||||
if (Modernizr.ios) {
|
||||
const scrollable = document.querySelectorAll("[data-md-scrollfix]")
|
||||
Array.prototype.forEach.call(scrollable, item => {
|
||||
item.addEventListener("touchstart", () => {
|
||||
const top = item.scrollTop
|
||||
|
||||
/* We're at the top of the container */
|
||||
if (top === 0) {
|
||||
item.scrollTop = 1
|
||||
/* We're at the top of the container */
|
||||
if (top === 0) {
|
||||
item.scrollTop = 1
|
||||
|
||||
/* We're at the bottom of the container */
|
||||
} else if (top + item.offsetHeight === item.scrollHeight) {
|
||||
item.scrollTop = top - 1
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}).listen()
|
||||
|
||||
/* Component: sidebar container */
|
||||
if (!Modernizr.csscalc)
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"resize", "orientationchange"
|
||||
], new Material.Sidebar.Container("[data-md-component=container]")))
|
||||
|
||||
/* Component: sidebar with navigation */
|
||||
new Material.Event.MatchMedia("(min-width: 1220px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Sidebar.Position("[data-md-component=navigation]")))
|
||||
|
||||
/* Component: sidebar with table of contents */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Sidebar.Position("[data-md-component=toc]")))
|
||||
|
||||
/* Component: link blurring for table of contents */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(window, "scroll",
|
||||
new Material.Nav.Blur("[data-md-component=toc] .md-nav__link")))
|
||||
|
||||
/* Component: collapsible elements for navigation */
|
||||
const collapsibles =
|
||||
document.querySelectorAll("[data-md-component=collapsible]")
|
||||
Array.prototype.forEach.call(collapsibles, collapse => {
|
||||
new Material.Event.MatchMedia("(min-width: 1220px)",
|
||||
new Material.Event.Listener(collapse.previousElementSibling, "click",
|
||||
new Material.Nav.Collapse(collapse)))
|
||||
})
|
||||
|
||||
/* Component: active pane monitor for iOS scrolling fixes */
|
||||
new Material.Event.MatchMedia("(max-width: 1219px)",
|
||||
new Material.Event.Listener(
|
||||
"[data-md-component=navigation] [data-md-toggle]", "change",
|
||||
new Material.Nav.Scrolling("[data-md-component=navigation] nav")))
|
||||
|
||||
/* Component: search body lock for mobile */
|
||||
new Material.Event.MatchMedia("(max-width: 959px)",
|
||||
new Material.Event.Listener("[data-md-toggle=search]", "change",
|
||||
new Material.Search.Lock("[data-md-toggle=search]")))
|
||||
|
||||
/* Component: search results */
|
||||
new Material.Event.Listener(document.forms.search.query, [
|
||||
"focus", "keyup"
|
||||
], new Material.Search.Result("[data-md-component=result]", () => {
|
||||
return fetch(`${this.config_.url.base}/mkdocs/search_index.json`, {
|
||||
credentials: "same-origin"
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
return data.docs.map(doc => {
|
||||
doc.location = this.config_.url.base + doc.location
|
||||
return doc
|
||||
})
|
||||
})
|
||||
})).listen()
|
||||
|
||||
/* Listener: prevent touches on overlay if navigation is active */
|
||||
new Material.Event.MatchMedia("(max-width: 1219px)",
|
||||
new Material.Event.Listener("[data-md-component=overlay]", "touchstart",
|
||||
ev => ev.preventDefault()))
|
||||
|
||||
/* Listener: close drawer when anchor links are clicked */
|
||||
new Material.Event.MatchMedia("(max-width: 959px)",
|
||||
new Material.Event.Listener("[data-md-component=navigation] [href^='#']",
|
||||
"click", () => {
|
||||
const toggle = document.querySelector("[data-md-toggle=drawer]")
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
} else if (top + item.offsetHeight === item.scrollHeight) {
|
||||
item.scrollTop = top - 1
|
||||
}
|
||||
}))
|
||||
|
||||
/* Listener: focus input after opening search */
|
||||
new Material.Event.Listener("[data-md-toggle=search]", "change", ev => {
|
||||
setTimeout(toggle => {
|
||||
const query = document.forms.search.query
|
||||
if (toggle.checked)
|
||||
query.focus()
|
||||
}, 400, ev.target)
|
||||
}).listen()
|
||||
|
||||
/* Listener: open search on focus */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(document.forms.search.query, "focus", () => {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (!toggle.checked) {
|
||||
toggle.checked = true
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
}
|
||||
}))
|
||||
|
||||
/* Listener: close search when clicking outside */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(document.body, "click", () => {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
}
|
||||
}))
|
||||
|
||||
/* Listener: disable search when ESC key is pressed */
|
||||
new Material.Event.Listener(window, "keyup", ev => {
|
||||
const code = ev.keyCode || ev.which
|
||||
if (code === 27) {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
document.forms.search.query.blur()
|
||||
}
|
||||
}
|
||||
}).listen()
|
||||
|
||||
/* Listener: fix unclickable toggle due to blur handler */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener("[data-md-toggle=search]", "click",
|
||||
ev => ev.stopPropagation()))
|
||||
|
||||
/* Listener: prevent search from closing when clicking */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener("[data-md-component=search]", "click",
|
||||
ev => ev.stopPropagation()))
|
||||
|
||||
/* Retrieve facts for the given repository type */
|
||||
;(() => {
|
||||
const el = document.querySelector("[data-md-source]")
|
||||
if (!el) return Promise.resolve([])
|
||||
switch (el.dataset.mdSource) {
|
||||
case "github": return new Material.Source.Adapter.GitHub(el).fetch()
|
||||
default: return Promise.resolve([])
|
||||
}
|
||||
|
||||
/* Render repository source information */
|
||||
})().then(facts => {
|
||||
const sources = document.querySelectorAll("[data-md-source]")
|
||||
Array.prototype.forEach.call(sources, source => {
|
||||
new Material.Source.Repository(source)
|
||||
.initialize(facts)
|
||||
})
|
||||
})
|
||||
}
|
||||
}).listen()
|
||||
|
||||
/* Component: header shadow toggle */
|
||||
new Material.Event.MatchMedia("(min-width: 1220px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Header.Shadow("[data-md-component=container]")))
|
||||
|
||||
/* Component: tabs visibility toggle */
|
||||
if (document.querySelector("[data-md-component=tabs]"))
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Tabs.Toggle("[data-md-component=tabs]")).listen()
|
||||
|
||||
/* Component: sidebar with navigation */
|
||||
new Material.Event.MatchMedia("(min-width: 1220px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Sidebar.Position("[data-md-component=navigation]")))
|
||||
|
||||
/* Component: sidebar with table of contents - register two separate
|
||||
listeners, as the offset at the top might change */
|
||||
new Material.Event.MatchMedia("(min-width: 960px) and (max-width: 1219px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Sidebar.Position("[data-md-component=toc]")))
|
||||
new Material.Event.MatchMedia("(min-width: 1220px)",
|
||||
new Material.Event.Listener(window, [
|
||||
"scroll", "resize", "orientationchange"
|
||||
], new Material.Sidebar.Position("[data-md-component=toc]")))
|
||||
|
||||
/* Component: link blurring for table of contents */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(window, "scroll",
|
||||
new Material.Nav.Blur("[data-md-component=toc] [href]")))
|
||||
|
||||
/* Component: collapsible elements for navigation */
|
||||
const collapsibles =
|
||||
document.querySelectorAll("[data-md-component=collapsible]")
|
||||
Array.prototype.forEach.call(collapsibles, collapse => {
|
||||
new Material.Event.MatchMedia("(min-width: 1220px)",
|
||||
new Material.Event.Listener(collapse.previousElementSibling, "click",
|
||||
new Material.Nav.Collapse(collapse)))
|
||||
})
|
||||
|
||||
/* Component: active pane monitor for iOS scrolling fixes */
|
||||
new Material.Event.MatchMedia("(max-width: 1219px)",
|
||||
new Material.Event.Listener(
|
||||
"[data-md-component=navigation] [data-md-toggle]", "change",
|
||||
new Material.Nav.Scrolling("[data-md-component=navigation] nav")))
|
||||
|
||||
/* Component: search body lock for mobile */
|
||||
new Material.Event.MatchMedia("(max-width: 959px)",
|
||||
new Material.Event.Listener("[data-md-toggle=search]", "change",
|
||||
new Material.Search.Lock("[data-md-toggle=search]")))
|
||||
|
||||
/* Component: search results */
|
||||
new Material.Event.Listener("[data-md-component=query]", [
|
||||
"focus", "keyup"
|
||||
], new Material.Search.Result("[data-md-component=result]", () => {
|
||||
return fetch(`${config.url.base}/mkdocs/search_index.json`, {
|
||||
credentials: "same-origin"
|
||||
}).then(response => response.json())
|
||||
.then(data => {
|
||||
return data.docs.map(doc => {
|
||||
doc.location = config.url.base + doc.location
|
||||
return doc
|
||||
})
|
||||
})
|
||||
})).listen()
|
||||
|
||||
/* Listener: prevent touches on overlay if navigation is active */
|
||||
new Material.Event.MatchMedia("(max-width: 1219px)",
|
||||
new Material.Event.Listener("[data-md-component=overlay]", "touchstart",
|
||||
ev => ev.preventDefault()))
|
||||
|
||||
/* Listener: close drawer when anchor links are clicked */
|
||||
new Material.Event.MatchMedia("(max-width: 959px)",
|
||||
new Material.Event.Listener("[data-md-component=navigation] [href^='#']",
|
||||
"click", () => {
|
||||
const toggle = document.querySelector("[data-md-toggle=drawer]")
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
}
|
||||
}))
|
||||
|
||||
/* Listener: focus input after opening search */
|
||||
new Material.Event.Listener("[data-md-toggle=search]", "change", ev => {
|
||||
setTimeout(toggle => {
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
const query = document.querySelector("[data-md-component=query]")
|
||||
if (!(query instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
query.focus()
|
||||
}
|
||||
}, 400, ev.target)
|
||||
}).listen()
|
||||
|
||||
/* Listener: open search on focus */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener("[data-md-component=query]", "focus", () => {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (!toggle.checked) {
|
||||
toggle.checked = true
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
}
|
||||
}))
|
||||
|
||||
/* Listener: close search when clicking outside */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener(document.body, "click", () => {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
}
|
||||
}))
|
||||
|
||||
/* Listener: disable search when ESC key is pressed */
|
||||
new Material.Event.Listener(window, "keyup", ev => {
|
||||
const code = ev.keyCode || ev.which
|
||||
if (code === 27) {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
const query = document.querySelector("[data-md-component=query]")
|
||||
if (!(query instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
query.focus()
|
||||
}
|
||||
}
|
||||
}).listen()
|
||||
|
||||
/* Listener: fix unclickable toggle due to blur handler */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener("[data-md-toggle=search]", "click",
|
||||
ev => ev.stopPropagation()))
|
||||
|
||||
/* Listener: prevent search from closing when clicking */
|
||||
new Material.Event.MatchMedia("(min-width: 960px)",
|
||||
new Material.Event.Listener("[data-md-component=search]", "click",
|
||||
ev => ev.stopPropagation()))
|
||||
|
||||
/* Retrieve facts for the given repository type */
|
||||
;(() => {
|
||||
const el = document.querySelector("[data-md-source]")
|
||||
if (!el)
|
||||
return Promise.resolve([])
|
||||
else if (!(el instanceof HTMLAnchorElement))
|
||||
throw new ReferenceError
|
||||
switch (el.dataset.mdSource) {
|
||||
case "github": return new Material.Source.Adapter.GitHub(el).fetch()
|
||||
default: return Promise.resolve([])
|
||||
}
|
||||
|
||||
/* Render repository information */
|
||||
})().then(facts => {
|
||||
const sources = document.querySelectorAll("[data-md-source]")
|
||||
Array.prototype.forEach.call(sources, source => {
|
||||
new Material.Source.Repository(source)
|
||||
.initialize(facts)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Exports
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export {
|
||||
initialize
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
*/
|
||||
|
||||
import Event from "./Material/Event"
|
||||
import Header from "./Material/Header"
|
||||
import Nav from "./Material/Nav"
|
||||
import Search from "./Material/Search"
|
||||
import Sidebar from "./Material/Sidebar"
|
||||
import Source from "./Material/Source"
|
||||
import Tabs from "./Material/Tabs"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Module
|
||||
@@ -32,8 +34,10 @@ import Source from "./Material/Source"
|
||||
|
||||
export default {
|
||||
Event,
|
||||
Header,
|
||||
Nav,
|
||||
Search,
|
||||
Sidebar,
|
||||
Source
|
||||
Source,
|
||||
Tabs
|
||||
}
|
||||
|
||||
@@ -30,14 +30,22 @@ export default class Listener {
|
||||
* Generic event listener
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|NodeList<HTMLElement>)} els - Selector or HTML elements
|
||||
* @param {Array.<string>} events - Event names
|
||||
* @param {(object|function)} handler - Handler to be invoked
|
||||
*
|
||||
* @property {(Array<EventTarget>)} els_ - Event targets
|
||||
* @property {Object} handler_- Event handlers
|
||||
* @property {Array<string>} events_ - Event names
|
||||
* @property {Function} update_ - Update handler
|
||||
*
|
||||
* @param {?(string|EventTarget|NodeList<EventTarget>)} els -
|
||||
* Selector or Event targets
|
||||
* @param {(string|Array<string>)} events - Event names
|
||||
* @param {(Object|Function)} handler - Handler to be invoked
|
||||
*/
|
||||
constructor(els, events, handler) {
|
||||
this.els_ = (typeof els === "string")
|
||||
? document.querySelectorAll(els)
|
||||
: [].concat(els)
|
||||
this.els_ = Array.prototype.slice.call(
|
||||
(typeof els === "string")
|
||||
? document.querySelectorAll(els)
|
||||
: [].concat(els))
|
||||
|
||||
/* Set handler as function or directly as object */
|
||||
this.handler_ = typeof handler === "function"
|
||||
@@ -53,7 +61,7 @@ export default class Listener {
|
||||
* Register listener for all relevant events
|
||||
*/
|
||||
listen() {
|
||||
Array.prototype.forEach.call(this.els_, el => {
|
||||
this.els_.forEach(el => {
|
||||
this.events_.forEach(event => {
|
||||
el.addEventListener(event, this.update_, false)
|
||||
})
|
||||
@@ -68,7 +76,7 @@ export default class Listener {
|
||||
* Unregister listener for all relevant events
|
||||
*/
|
||||
unlisten() {
|
||||
Array.prototype.forEach.call(this.els_, el => {
|
||||
this.els_.forEach(el => {
|
||||
this.events_.forEach(event => {
|
||||
el.removeEventListener(event, this.update_)
|
||||
})
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Listener from "./Listener" // eslint-disable-line no-unused-vars
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Class
|
||||
* ------------------------------------------------------------------------- */
|
||||
@@ -33,6 +35,9 @@ export default class MatchMedia {
|
||||
* switches the given listeners on or off.
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {Function} handler_ - Media query event handler
|
||||
*
|
||||
* @param {string} query - Media query to test for
|
||||
* @param {Listener} listener - Event listener
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Shadow from "./Header/Shadow"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Module
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default {
|
||||
Shadow
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Class
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default class Shadow {
|
||||
|
||||
/**
|
||||
* Show or hide header shadow depending on page y-offset
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Content container
|
||||
* @property {HTMLElement} header_ - Header
|
||||
* @property {number} height_ - Offset height of previous nodes
|
||||
* @property {boolean} active_ - Header shadow state
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLElement) ||
|
||||
!(ref.parentNode instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Grab parent and header */
|
||||
this.el_ = ref.parentNode
|
||||
if (!(this.el_.parentNode instanceof HTMLElement) ||
|
||||
!(this.el_.parentNode.previousElementSibling instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.header_ = this.el_.parentNode.previousElementSibling
|
||||
|
||||
/* Initialize height and state */
|
||||
this.height_ = 0
|
||||
this.active_ = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total height of previous nodes
|
||||
*/
|
||||
setup() {
|
||||
let current = this.el_
|
||||
while ((current = current.previousElementSibling)) {
|
||||
if (!(current instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.height_ += current.offsetHeight
|
||||
}
|
||||
this.update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update shadow state
|
||||
*/
|
||||
update() {
|
||||
const active = window.pageYOffset >= this.height_
|
||||
if (active !== this.active_)
|
||||
this.header_.dataset.mdState = (this.active_ = active) ? "shadow" : ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset shadow state
|
||||
*/
|
||||
reset() {
|
||||
this.header_.dataset.mdState = ""
|
||||
this.height_ = 0
|
||||
this.active_ = false
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,16 @@
|
||||
export default class Blur {
|
||||
|
||||
/**
|
||||
* Blur anchors within the navigation above current page y-offset
|
||||
* Blur links within the table of contents above current page y-offset
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {NodeList<HTMLElement>} els_ - Table of contents links
|
||||
* @property {Array<HTMLElement>} anchors_ - Referenced anchor nodes
|
||||
* @property {number} index_ - Current link index
|
||||
* @property {number} offset_ - Current page y-offset
|
||||
* @property {boolean} dir_ - Scroll direction change
|
||||
*
|
||||
* @param {(string|NodeList<HTMLElement>)} els - Selector or HTML elements
|
||||
*/
|
||||
constructor(els) {
|
||||
@@ -38,27 +45,28 @@ export default class Blur {
|
||||
: els
|
||||
|
||||
/* Initialize index and page y-offset */
|
||||
this.index_ = 0
|
||||
this.index_ = 0
|
||||
this.offset_ = window.pageYOffset
|
||||
|
||||
/* Necessary state to correctly reset the index */
|
||||
this.dir_ = false
|
||||
|
||||
/* Index anchor node offsets for fast lookup */
|
||||
this.anchors_ = [].map.call(this.els_, el => {
|
||||
return document.getElementById(el.hash.substring(1))
|
||||
})
|
||||
this.anchors_ = [].reduce.call(this.els_, (anchors, el) => {
|
||||
return anchors.concat(
|
||||
document.getElementById(el.hash.substring(1)) || [])
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize anchor states
|
||||
* Initialize blur states
|
||||
*/
|
||||
setup() {
|
||||
this.update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update anchor states
|
||||
* Update blur states
|
||||
*
|
||||
* Deduct the static offset of the header (56px) and sidebar offset (24px),
|
||||
* see _permalinks.scss for more information.
|
||||
@@ -67,7 +75,7 @@ export default class Blur {
|
||||
const offset = window.pageYOffset
|
||||
const dir = this.offset_ - offset < 0
|
||||
|
||||
/* Hack: reset index if direction changed, to catch very fast scrolling,
|
||||
/* Hack: reset index if direction changed to catch very fast scrolling,
|
||||
because otherwise we would have to register a timer and that sucks */
|
||||
if (this.dir_ !== dir)
|
||||
this.index_ = dir
|
||||
@@ -109,7 +117,7 @@ export default class Blur {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset anchor states
|
||||
* Reset blur states
|
||||
*/
|
||||
reset() {
|
||||
Array.prototype.forEach.call(this.els_, el => {
|
||||
|
||||
@@ -30,12 +30,18 @@ export default class Collapse {
|
||||
* Expand or collapse navigation on toggle
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Navigation list
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,11 +81,16 @@ export default class Collapse {
|
||||
|
||||
/* Remove state on end of transition */
|
||||
const end = ev => {
|
||||
ev.target.removeAttribute("data-md-state")
|
||||
ev.target.style.maxHeight = ""
|
||||
const target = ev.target
|
||||
if (!(target instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Reset height and state */
|
||||
target.removeAttribute("data-md-state")
|
||||
target.style.maxHeight = ""
|
||||
|
||||
/* Only fire once, so directly remove event listener */
|
||||
ev.target.removeEventListener("transitionend", end)
|
||||
target.removeEventListener("transitionend", end)
|
||||
}
|
||||
this.el_.addEventListener("transitionend", end, false)
|
||||
}
|
||||
|
||||
@@ -30,12 +30,18 @@ export default class Scrolling {
|
||||
* Set overflow scrolling on the current active pane (for iOS)
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Primary navigation
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,13 +55,22 @@ export default class Scrolling {
|
||||
/* Find all toggles and check which one is active */
|
||||
const toggles = this.el_.querySelectorAll("[data-md-toggle]")
|
||||
Array.prototype.forEach.call(toggles, toggle => {
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
|
||||
/* Find corresponding navigational pane */
|
||||
let pane = toggle.nextElementSibling
|
||||
while (pane.tagName !== "NAV")
|
||||
if (!(pane instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
while (pane.tagName !== "NAV" && pane.nextElementSibling)
|
||||
pane = pane.nextElementSibling
|
||||
|
||||
/* Check references */
|
||||
if (!(toggle.parentNode instanceof HTMLElement) ||
|
||||
!(toggle.parentNode.parentNode instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Find current and parent list elements */
|
||||
const parent = toggle.parentNode.parentNode
|
||||
const target = pane.children[pane.children.length - 1]
|
||||
@@ -73,34 +88,48 @@ export default class Scrolling {
|
||||
* @param {Event} ev - Change event
|
||||
*/
|
||||
update(ev) {
|
||||
const target = ev.target
|
||||
if (!(target instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Find corresponding navigational pane */
|
||||
let pane = ev.target.nextElementSibling
|
||||
while (pane.tagName !== "NAV")
|
||||
let pane = target.nextElementSibling
|
||||
if (!(pane instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
while (pane.tagName !== "NAV" && pane.nextElementSibling)
|
||||
pane = pane.nextElementSibling
|
||||
|
||||
/* Find current and parent list elements */
|
||||
const parent = ev.target.parentNode.parentNode
|
||||
const target = pane.children[pane.children.length - 1]
|
||||
/* Check references */
|
||||
if (!(target.parentNode instanceof HTMLElement) ||
|
||||
!(target.parentNode.parentNode instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Find parent and active panes */
|
||||
const parent = target.parentNode.parentNode
|
||||
const active = pane.children[pane.children.length - 1]
|
||||
|
||||
/* Always reset all lists when transitioning */
|
||||
parent.style.webkitOverflowScrolling = ""
|
||||
target.style.webkitOverflowScrolling = ""
|
||||
active.style.webkitOverflowScrolling = ""
|
||||
|
||||
/* Set overflow scrolling on parent */
|
||||
if (!ev.target.checked) {
|
||||
/* Set overflow scrolling on parent pane */
|
||||
if (!target.checked) {
|
||||
const end = () => {
|
||||
parent.style.webkitOverflowScrolling = "touch"
|
||||
pane.removeEventListener("transitionend", end)
|
||||
if (pane instanceof HTMLElement) {
|
||||
parent.style.webkitOverflowScrolling = "touch"
|
||||
pane.removeEventListener("transitionend", end)
|
||||
}
|
||||
}
|
||||
pane.addEventListener("transitionend", end, false)
|
||||
}
|
||||
|
||||
/* Set overflow scrolling on target */
|
||||
if (ev.target.checked) {
|
||||
/* Set overflow scrolling on active pane */
|
||||
if (target.checked) {
|
||||
const end = () => {
|
||||
target.style.webkitOverflowScrolling = "touch"
|
||||
pane.removeEventListener("transitionend", end, false)
|
||||
if (pane instanceof HTMLElement) {
|
||||
active.style.webkitOverflowScrolling = "touch"
|
||||
pane.removeEventListener("transitionend", end)
|
||||
}
|
||||
}
|
||||
pane.addEventListener("transitionend", end, false)
|
||||
}
|
||||
@@ -117,20 +146,29 @@ export default class Scrolling {
|
||||
/* Find all toggles and check which one is active */
|
||||
const toggles = this.el_.querySelectorAll("[data-md-toggle]")
|
||||
Array.prototype.forEach.call(toggles, toggle => {
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
|
||||
/* Find corresponding navigational pane */
|
||||
let pane = toggle.nextElementSibling
|
||||
while (pane.tagName !== "NAV")
|
||||
if (!(pane instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
while (pane.tagName !== "NAV" && pane.nextElementSibling)
|
||||
pane = pane.nextElementSibling
|
||||
|
||||
/* Find current and parent list elements */
|
||||
/* Check references */
|
||||
if (!(toggle.parentNode instanceof HTMLElement) ||
|
||||
!(toggle.parentNode.parentNode instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Find parent and active panes */
|
||||
const parent = toggle.parentNode.parentNode
|
||||
const target = pane.children[pane.children.length - 1]
|
||||
const active = pane.children[pane.children.length - 1]
|
||||
|
||||
/* Always reset all lists when transitioning */
|
||||
parent.style.webkitOverflowScrolling = ""
|
||||
target.style.webkitOverflowScrolling = ""
|
||||
active.style.webkitOverflowScrolling = ""
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,12 +30,25 @@ export default class Lock {
|
||||
* Lock body for full-screen search modal
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLInputElement} el_ - Lock toggle
|
||||
* @property {HTMLElement} lock_ - Element to lock (document body)
|
||||
* @property {number} offset_ - Current page y-offset
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
|
||||
/* Retrieve element to lock (= body) */
|
||||
if (!document.body)
|
||||
throw new ReferenceError
|
||||
this.lock_ = document.body
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,13 +73,13 @@ export default class Lock {
|
||||
|
||||
/* Lock body after finishing transition */
|
||||
if (this.el_.checked) {
|
||||
document.body.dataset.mdState = "lock"
|
||||
this.lock_.dataset.mdState = "lock"
|
||||
}
|
||||
}, 400)
|
||||
|
||||
/* Exiting search mode */
|
||||
} else {
|
||||
document.body.dataset.mdState = ""
|
||||
this.lock_.dataset.mdState = ""
|
||||
|
||||
/* Scroll to former position, but wait for 100ms to prevent flashes on
|
||||
iOS. A short timeout seems to do the trick */
|
||||
@@ -81,8 +94,8 @@ export default class Lock {
|
||||
* Reset locked state and page y-offset
|
||||
*/
|
||||
reset() {
|
||||
if (document.body.dataset.mdState === "lock")
|
||||
if (this.lock_.dataset.mdState === "lock")
|
||||
window.scrollTo(0, this.offset_)
|
||||
document.body.dataset.mdState = ""
|
||||
this.lock_.dataset.mdState = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,24 @@ export default class Result {
|
||||
* Perform search and update results on keyboard events
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Search result container
|
||||
* @property {(Array<Object>|Function)} data_ - Raw document data
|
||||
* @property {Object} docs_ - Indexed documents
|
||||
* @property {HTMLElement} meta_ - Search meta information
|
||||
* @property {HTMLElement} list_ - Search result list
|
||||
* @property {Object} index_ - Search index
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
* @param {(Array.<object>|Function)} data - Promise or array providing data
|
||||
* @param {(Array<Object>|Function)} data - Function providing data or array
|
||||
*/
|
||||
constructor(el, data) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
|
||||
/* Set data and create metadata and list elements */
|
||||
this.data_ = data
|
||||
@@ -54,19 +65,26 @@ export default class Result {
|
||||
/* Inject created elements */
|
||||
this.el_.appendChild(this.meta_)
|
||||
this.el_.appendChild(this.list_)
|
||||
}
|
||||
|
||||
/* Truncate a string after the given number of characters - this is not
|
||||
a reasonable approach, since the summaries kind of suck. It would be
|
||||
better to create something more intelligent, highlighting the search
|
||||
occurrences and making a better summary out of it */
|
||||
this.truncate_ = function(string, n) {
|
||||
let i = n
|
||||
if (string.length > i) {
|
||||
while (string[i] !== " " && --i > 0);
|
||||
return `${string.substring(0, i)}...`
|
||||
}
|
||||
return string
|
||||
/**
|
||||
* Truncate a string after the given number of character
|
||||
*
|
||||
* This is not a reasonable approach, since the summaries kind of suck. It
|
||||
* would be better to create something more intelligent, highlighting the
|
||||
* search occurrences and making a better summary out of it
|
||||
*
|
||||
* @param {string} string - String to be truncated
|
||||
* @param {number} n - Number of characters
|
||||
* @return {string} Truncated string
|
||||
*/
|
||||
truncate_(string, n) {
|
||||
let i = n
|
||||
if (string.length > i) {
|
||||
while (string[i] !== " " && --i > 0);
|
||||
return `${string.substring(0, i)}...`
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +108,7 @@ export default class Result {
|
||||
})
|
||||
|
||||
/* Index documents */
|
||||
this.data_ = data.reduce((docs, doc) => {
|
||||
this.docs_ = data.reduce((docs, doc) => {
|
||||
this.index_.add(doc)
|
||||
docs[doc.location] = doc
|
||||
return docs
|
||||
@@ -104,15 +122,20 @@ export default class Result {
|
||||
: init(this.data_)
|
||||
}, 250)
|
||||
|
||||
/* Execute search on new input event after clearing current list */
|
||||
/* Execute search on new input event */
|
||||
} else if (ev.type === "keyup") {
|
||||
const target = ev.target
|
||||
if (!(target instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
|
||||
/* Clear current list */
|
||||
while (this.list_.firstChild)
|
||||
this.list_.removeChild(this.list_.firstChild)
|
||||
|
||||
/* Perform search on index and render documents */
|
||||
const result = this.index_.search(ev.target.value)
|
||||
const result = this.index_.search(target.value)
|
||||
result.forEach(item => {
|
||||
const doc = this.data_[item.ref]
|
||||
const doc = this.docs_[item.ref]
|
||||
|
||||
/* Check if it's a anchor link on the current page */
|
||||
let [pathname] = doc.location.split("#")
|
||||
@@ -143,6 +166,8 @@ export default class Result {
|
||||
Array.prototype.forEach.call(anchors, anchor => {
|
||||
anchor.addEventListener("click", ev2 => {
|
||||
const toggle = document.querySelector("[data-md-toggle=search]")
|
||||
if (!(toggle instanceof HTMLInputElement))
|
||||
throw new ReferenceError
|
||||
if (toggle.checked) {
|
||||
toggle.checked = false
|
||||
toggle.dispatchEvent(new CustomEvent("change"))
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Container from "./Sidebar/Container"
|
||||
import Position from "./Sidebar/Position"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
@@ -28,6 +27,5 @@ import Position from "./Sidebar/Position"
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default {
|
||||
Container,
|
||||
Position
|
||||
}
|
||||
|
||||
@@ -30,15 +30,25 @@ export default class Position {
|
||||
* Set sidebars to locked state and limit height to parent node
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Sidebar
|
||||
* @property {HTMLElement} parent_ - Sidebar container
|
||||
* @property {number} height_ - Current sidebar height
|
||||
* @property {number} offset_ - Current page y-offset
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLElement) ||
|
||||
!(ref.parentNode instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
|
||||
/* Initialize parent container and current height */
|
||||
this.parent_ = this.el_.parentNode
|
||||
this.parent_ = ref.parentNode
|
||||
this.height_ = 0
|
||||
}
|
||||
|
||||
@@ -46,7 +56,7 @@ export default class Position {
|
||||
* Initialize sidebar state
|
||||
*/
|
||||
setup() {
|
||||
this.offset_ = this.el_.offsetTop - this.parent_.offsetTop
|
||||
this.offset_ = this.el_.offsetTop - 56
|
||||
this.update()
|
||||
}
|
||||
|
||||
@@ -55,25 +65,25 @@ export default class Position {
|
||||
*
|
||||
* The inner height of the window (= the visible area) is the maximum
|
||||
* possible height for the stretching sidebar. This height must be deducted
|
||||
* by the top offset of the parent container, which accounts for the fixed
|
||||
* header, setting the baseline. Depending on the page y-offset, the top
|
||||
* offset of the sidebar must be taken into account, as well as the case
|
||||
* where the window is scrolled beyond the sidebar container.
|
||||
* by the height of the fixed header (56px). Depending on the page y-offset,
|
||||
* the top offset of the sidebar must be taken into account, as well as the
|
||||
* case where the window is scrolled beyond the sidebar container.
|
||||
*/
|
||||
update() {
|
||||
const offset = window.pageYOffset
|
||||
const visible = window.innerHeight
|
||||
|
||||
/* Calculate bounds of sidebar container */
|
||||
this.bounds_ = {
|
||||
top: this.parent_.offsetTop,
|
||||
/* Set bounds of sidebar container - must be calculated on every run, as
|
||||
the height of the content might change due to loading images etc. */
|
||||
const bounds = {
|
||||
top: 56,
|
||||
bottom: this.parent_.offsetTop + this.parent_.offsetHeight
|
||||
}
|
||||
|
||||
/* Calculate new offset and height */
|
||||
const height = visible - this.bounds_.top
|
||||
const height = visible - bounds.top
|
||||
- Math.max(0, this.offset_ - offset)
|
||||
- Math.max(0, offset + visible - this.bounds_.bottom)
|
||||
- Math.max(0, offset + visible - bounds.bottom)
|
||||
|
||||
/* If height changed, update element */
|
||||
if (height !== this.height_)
|
||||
|
||||
@@ -29,16 +29,25 @@ import Cookies from "js-cookie"
|
||||
export default class Abstract {
|
||||
|
||||
/**
|
||||
* Retrieve source information
|
||||
* Retrieve repository information
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*
|
||||
* @property {HTMLAnchorElement} el_ - Link to repository
|
||||
* @property {string} base_ - API base URL
|
||||
* @property {number} salt_ - Unique identifier
|
||||
*
|
||||
* @param {(string|HTMLAnchorElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
|
||||
if (!(ref instanceof HTMLAnchorElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
|
||||
/* Retrieve base URL */
|
||||
this.base_ = this.el_.href
|
||||
this.salt_ = this.hash_(this.base_)
|
||||
@@ -47,7 +56,7 @@ export default class Abstract {
|
||||
/**
|
||||
* Retrieve data from Cookie or fetch from respective API
|
||||
*
|
||||
* @return {Promise} Promise that returns an array of facts
|
||||
* @return {Promise<Array<string>>} Promise that returns an array of facts
|
||||
*/
|
||||
fetch() {
|
||||
return new Promise(resolve => {
|
||||
@@ -70,7 +79,6 @@ export default class Abstract {
|
||||
* Abstract private function that fetches relevant repository information
|
||||
*
|
||||
* @abstract
|
||||
* @return {Promise} Promise that provides the facts in an array
|
||||
*/
|
||||
fetch_() {
|
||||
throw new Error("fetch_(): Not implemented")
|
||||
@@ -79,15 +87,15 @@ export default class Abstract {
|
||||
/**
|
||||
* Format a number with suffix
|
||||
*
|
||||
* @param {Number} number - Number to format
|
||||
* @return {Number} Formatted number
|
||||
* @param {number} number - Number to format
|
||||
* @return {string} Formatted number
|
||||
*/
|
||||
format_(number) {
|
||||
if (number > 10000)
|
||||
return `${(number / 1000).toFixed(0)}k`
|
||||
else if (number > 1000)
|
||||
return `${(number / 1000).toFixed(1)}k`
|
||||
return number
|
||||
return `${number}`
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +104,7 @@ export default class Abstract {
|
||||
* Taken from http://stackoverflow.com/a/7616484/1065584
|
||||
*
|
||||
* @param {string} str - Input string
|
||||
* @return {string} Hashed string
|
||||
* @return {number} Hashed string
|
||||
*/
|
||||
hash_(str) {
|
||||
let hash = 0
|
||||
|
||||
@@ -29,10 +29,10 @@ import Abstract from "./Abstract"
|
||||
export default class GitHub extends Abstract {
|
||||
|
||||
/**
|
||||
* Retrieve source information from GitHub
|
||||
* Retrieve repository information from GitHub
|
||||
*
|
||||
* @constructor
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
* @param {(string|HTMLAnchorElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
super(el)
|
||||
@@ -42,9 +42,9 @@ export default class GitHub extends Abstract {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch relevant source information from GitHub
|
||||
* Fetch relevant repository information from GitHub
|
||||
*
|
||||
* @return {function} Promise returning an array of facts
|
||||
* @return {Promise<Array<string>>} Promise returning an array of facts
|
||||
*/
|
||||
fetch_() {
|
||||
return fetch(this.base_)
|
||||
|
||||
@@ -30,21 +30,27 @@ export default class Repository {
|
||||
* Render repository information
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Repository information
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof HTMLElement))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the source repository
|
||||
* Initialize the repository
|
||||
*
|
||||
* @param {Array.<string>} facts - Facts to be rendered
|
||||
* @param {Array<string>} facts - Facts to be rendered
|
||||
*/
|
||||
initialize(facts) {
|
||||
if (facts.length)
|
||||
if (facts.length && this.el_.children.length)
|
||||
this.el_.children[this.el_.children.length - 1].appendChild(
|
||||
<ul class="md-source__facts">
|
||||
{facts.map(fact => <li class="md-source__fact">{fact}</li>)}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Toggle from "./Tabs/Toggle"
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Module
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default {
|
||||
Toggle
|
||||
}
|
||||
+21
-17
@@ -24,42 +24,46 @@
|
||||
* Class
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
export default class Container {
|
||||
export default class Toggle {
|
||||
|
||||
/**
|
||||
* Monitor window height to stretch sidebar container to viewport
|
||||
* Toggle tabs visibility depending on page y-offset
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @property {HTMLElement} el_ - Content container
|
||||
* @property {number} offset_ - Toggle page-y offset
|
||||
* @property {boolean} active_ - Tabs visibility
|
||||
*
|
||||
* @param {(string|HTMLElement)} el - Selector or HTML element
|
||||
*/
|
||||
constructor(el) {
|
||||
this.el_ = (typeof el === "string")
|
||||
const ref = (typeof el === "string")
|
||||
? document.querySelector(el)
|
||||
: el
|
||||
if (!(ref instanceof Node))
|
||||
throw new ReferenceError
|
||||
this.el_ = ref
|
||||
|
||||
/* Retrieve parent node */
|
||||
this.parent_ = this.el_.parentNode
|
||||
/* Initialize offset and state */
|
||||
this.offset_ = 5
|
||||
this.active_ = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize container state
|
||||
*/
|
||||
setup() {
|
||||
this.update()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update minimum height
|
||||
* Update visibility
|
||||
*/
|
||||
update() {
|
||||
const height = this.parent_.offsetHeight - this.el_.offsetTop
|
||||
this.el_.style.minHeight = `${height}px`
|
||||
const active = window.pageYOffset >= this.offset_
|
||||
if (active !== this.active_)
|
||||
this.el_.dataset.mdState = (this.active_ = active) ? "hidden" : ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset minimum height
|
||||
* Reset visibility
|
||||
*/
|
||||
reset() {
|
||||
this.el_.style.minHeight = ""
|
||||
this.el_.dataset.mdState = ""
|
||||
this.active_ = false
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,20 @@ button[data-md-color-accent] {
|
||||
border-left: 0.4rem solid $color;
|
||||
}
|
||||
}
|
||||
|
||||
// [screen +]: Set background color for tabs
|
||||
@include break-from-device(screen) {
|
||||
|
||||
// Tabs with outline
|
||||
.md-tabs {
|
||||
background: mix($color, $md-color-black, 75%);
|
||||
|
||||
// Fade-out tabs background upon scrolling
|
||||
&[data-md-state="hidden"] {
|
||||
background: $color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
@import "layout/search";
|
||||
@import "layout/sidebar";
|
||||
@import "layout/source";
|
||||
@import "layout/tabs";
|
||||
|
||||
@import "extensions/admonition";
|
||||
@import "extensions/codehilite";
|
||||
|
||||
@@ -34,14 +34,9 @@ body {
|
||||
body,
|
||||
input {
|
||||
color: $md-color-black;
|
||||
// font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-feature-settings: "kern", "onum", "liga";
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
|
||||
// Use system fonts, if browser doesn't support webfonts
|
||||
.no-fontface & {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
// Proportionally spaced fonts
|
||||
@@ -49,14 +44,9 @@ pre,
|
||||
code,
|
||||
kbd {
|
||||
color: $md-color-black;
|
||||
// font-family: "Roboto Mono", "Courier New", Courier, monospace;
|
||||
font-feature-settings: "kern", "onum", "liga";
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-weight: 400;
|
||||
|
||||
// Use system fonts, if browser doesn't support webfonts
|
||||
.no-fontface & {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -361,6 +351,11 @@ kbd {
|
||||
}
|
||||
}
|
||||
|
||||
// Definition lists
|
||||
dd {
|
||||
margin: 1em 0 1em 1.875em;
|
||||
}
|
||||
|
||||
// Limit width to container
|
||||
iframe,
|
||||
img,
|
||||
@@ -372,18 +367,18 @@ kbd {
|
||||
table:not([class]) {
|
||||
@include z-depth(2);
|
||||
|
||||
margin: 2em 0;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
border-radius: 0.2rem;
|
||||
font-size: ms(-1);
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
// Semi-cool overflow solution, in case JavaScript is not available
|
||||
.no-js & {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
margin: 0.8em 0;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
// Due to margin collapse because of the necessary inline-block hack, we
|
||||
// cannot increase the bottom margin on the table, so we just increase the
|
||||
// top margin on the following element
|
||||
& + * {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
// Table headings and cells
|
||||
@@ -419,16 +414,25 @@ kbd {
|
||||
}
|
||||
}
|
||||
|
||||
// Data table wrapper, in case JavaScript is available
|
||||
.md-typeset__table {
|
||||
margin: 1.6em -1.6rem;
|
||||
// Wrapper for scrolling on overflow
|
||||
&__scrollwrap {
|
||||
margin: 1em -1.6rem;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
// Data table wrapper, in case JavaScript is available
|
||||
.md-typeset__table {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5em;
|
||||
padding: 0 1.6rem;
|
||||
|
||||
// Data tables
|
||||
table {
|
||||
display: inline-block;
|
||||
margin: 0 1.6rem;
|
||||
display: table;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,18 +80,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Hide anchor for top-level heading, as it makes no sense
|
||||
h1[id] .headerlink {
|
||||
display: none;
|
||||
// Correct headline offset with padding due to margin layering
|
||||
h1[id] {
|
||||
padding-top: 0.8rem;
|
||||
|
||||
// Hide anchor for top-level heading, as it makes no sense
|
||||
.headerlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Correct anchor offset for link blurring
|
||||
@each $level, $delta in (
|
||||
h2: 0.2rem,
|
||||
h3: 0.4rem,
|
||||
h4: 0.6rem,
|
||||
h5: 1rem,
|
||||
h6: 1rem
|
||||
h2: 0.4rem,
|
||||
h3: 0.7rem,
|
||||
h4: 0.8rem,
|
||||
h5: 1.1rem,
|
||||
h6: 1.1rem
|
||||
) {
|
||||
|
||||
// Un-targeted anchor
|
||||
|
||||
@@ -93,6 +93,7 @@ hr {
|
||||
display: table;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 5.6rem;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
@@ -101,28 +102,11 @@ hr {
|
||||
display: table-row;
|
||||
height: 100%;
|
||||
|
||||
// Top-spacing to account for header and some additional padding
|
||||
// Increase top spacing of content area to give typography more room
|
||||
&__inner {
|
||||
margin-top: 5.6rem;
|
||||
min-height: 100%;
|
||||
padding-top: 2.4rem + 0.6rem;
|
||||
overflow: auto;
|
||||
|
||||
// If the browser supports calc(), no JavaScript is necessary
|
||||
.csscalc & {
|
||||
min-height: calc(100% - #{5.6rem - 3rem});
|
||||
|
||||
// Hack: Firefox doesn't correctly calculate min-height, as it takes the
|
||||
// top margin into account which leads to the container overflowing its
|
||||
// parent. For this reason we use this hack here to target only Firefox
|
||||
// and see if we can find a better solution later.
|
||||
|
||||
// stylelint-disable-next-line function-url-quotes
|
||||
@-moz-document url-prefix() {
|
||||
& {
|
||||
min-height: calc(100% - 5.6rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,11 +47,11 @@
|
||||
// [screen +]: Increase spacing
|
||||
@include break-from-device(screen) {
|
||||
margin: 2.4rem;
|
||||
}
|
||||
|
||||
// Hack: remove bottom spacing, due to margin collapse
|
||||
:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
// Hack: remove bottom spacing of last element, due to margin collapse
|
||||
> :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@
|
||||
padding-bottom: 0.8rem;
|
||||
transition: opacity 0.25s;
|
||||
|
||||
// [mobile landscape +]: Set proportional width
|
||||
@include break-from-device(mobile landscape) {
|
||||
// [tablet +]: Set proportional width
|
||||
@include break-from-device(tablet) {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@
|
||||
// Title
|
||||
.md-footer-nav__title {
|
||||
|
||||
// [mobile portrait -]: Hide title for previous page
|
||||
@include break-to-device(mobile portrait) {
|
||||
// [mobile -]: Hide title for previous page
|
||||
@include break-to-device(mobile) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,21 @@
|
||||
color: $md-color-white;
|
||||
z-index: 1;
|
||||
|
||||
// Always show shadow, in case JavaScript is not available
|
||||
.no-js & {
|
||||
@include z-depth(2);
|
||||
}
|
||||
|
||||
// [screen +]: Show shadow depending on scroll offset
|
||||
@include break-from-device(screen) {
|
||||
box-shadow: none;
|
||||
|
||||
// Show and animate shadow
|
||||
&[data-md-state="shadow"] {
|
||||
@include z-depth(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide for print
|
||||
@media print {
|
||||
display: none;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user