Starting on docs v3

This commit is contained in:
Joannis Orlandos 2017-09-06 21:08:38 +02:00
parent 1b3c613cc1
commit 056c3e6f12
29 changed files with 828 additions and 0 deletions

149
3.0/docs/async/overview.md Normal file
View File

@ -0,0 +1,149 @@
# Async
Async provides the fundament for asynchronous libraries. It consists of Futures and Streams.
## Promise and Future
When working with asynchronous APIs, one of the problems you'll face is not knowing when a variable is set.
When querying a database synchronously, the thread is blocked until a result has been received. At which point the result will be returned to you and the thread continues from where you left off querying the database.
```swift
let user = try database.fetchUser(named: "Admin")
print(user.username)
```
In the asynchronous world, you won't receive a result immediately. Instead, you'll receive a result in a callback.
```swift
// Callback `found` will receive the user. If an error occurred, the `onError` callback will be called instead.
try database.fetchUser(named: "Admin", found: { user in
print(user.username)
}, onError: { error in
print(error)
})
```
You can imagine code becoming complex. Difficult to read and comprehend.
Promises and futures are two types that this library introduces to solve this.
### Creating a promise
Promises are important if you're implementing a function that returns a result in the future, such as the database shown above.
Promises need to be created without a result. They can then be completed with the expectation or an error at any point.
You can extract a `future` from the `promise` that you can hand to the API consumer.
```swift
// the example `fetchUser` implementation
func fetchUser(named name: String) -> Future<User> {
// Creates a promise that can be fulfilled in the future
let promise = Promise<User>()
// Run a query asynchronously, looking for the user
asyncDatabaseQuery(where: "username" == name, onComplete: { databaseResult in
do {
// Initialize the user using the datbase result
// This can throw an error if the result is empty or invalid
let user = try User(decodingFrom: databaseResult)
// If initialization is successful, complete the promise.
//
// Completing the promise will notify the promise's associated future with this user
promise.complete(user)
} catch {
// If initialization is successful, fail the promise.
//
// Failing the promise will notify the promise's associated future with an error
promise.fail(error)
}
})
// After spawning the asynchronous operation, return the promise's associated future
//
// The future can then be used by the API consumer
return promise.future
}
```
### On future completion
When a promise completes, you can chain the result/error into a closure:
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// `.then`'s closure will be executed on success
future.then { user in
print(user.username)
// `.catch` will catch any error on failure
}.catch { error in
print(error)
}
```
### Catching specific errors
Sometimes you only care for specific errors, for example, for logging.
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// `.then`'s closure will be executed on success
future.then { user in
print(user.username)
// This `.catch` will only catch `DatabaseError`s
}.catch(DatabaseError.self) { databaseError in
print(databaseError)
// `.catch` will catch any error on failure, including `DatabaseError` types
}.catch { error in
print(error)
}
```
### Mapping results
Futures can be mapped to different results asynchronously.
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// Maps the user to it's username
let futureUsername: Future<String> = future.map { user in
return user.username
}
// Mapped futures can be mapped and chained, too
futureUsername.then { username in
print(username)
}
```
### For synchronous APIs
Sometimes, an API needs to be used synchronously in a synchronous envinronment.
Rather than using a synchronous API with all edge cases involved, we recommend using the `try future.sync()` function.
```swift
// The future provided by the above function will be used
let future: Future<User> = fetchUser(named: "Admin")
// This will either receive the user if the promise was completed or throw an error if the promise was failed.
let user: User = try future.sync()
```
This will wait for a result indefinitely, blocking the thread.
If you expect a result with a specified duration, say, 30 seconds:
```swift
// This will also throw an error if the deadline wasn't met
let user = try future.sync(deadline: .seconds(30))
```

30
3.0/docs/async/package.md Normal file
View File

@ -0,0 +1,30 @@
# Using Core
## With Vapor
This package is included with Vapor by default, just add:
```swift
import Async
```
## Without Vapor
Async is a powerful library for any Swift project. To include it in your package, add the following to your `Package.swift` file.
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.package(url: "https://github.com/vapor/async.git", majorVersion: 2),
],
targets: [
.target(name: "Project", dependencies: ["Async", ... ])
]
)
```
Use `import Async` to access Async's APIs.

138
3.0/docs/index.md Normal file
View File

@ -0,0 +1,138 @@
# Vapor Documentation
This is the documentation for Vapor, a Web Framework for Swift that works on iOS, macOS, and Ubuntu; and all of the packages that Vapor offers.
Vapor is the most used web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website or API.
## Getting Started
If this is your first time using Vapor, head to the [Getting Started](getting-started/install-on-macos.md) section to install Swift and create your first app.
## Like Vapor?
Our small team works hard to make Vapor awesome (and free). Support the framework by starring Vapor on GitHub or donating $1 monthly--it helps us a lot. Thanks!
<a href="https://github.com/vapor/vapor" target="_blank">
<img src="https://cloud.githubusercontent.com/assets/1342803/26243875/5490d02c-3c85-11e7-9667-d56eb97c2cf9.png" style="height:40px">
</a>
<a href="https://opencollective.com/vapor" target="_blank">
<img src="https://cloud.githubusercontent.com/assets/1342803/26243876/54913ce2-3c85-11e7-9848-121adbe92198.png" style="margin-left: 10px; height:40px">
</a>
## Other Sources
Here are some other great places to find information about Vapor.
### API
Auto-generated API documentation is located at [api.vapor.codes](http://api.vapor.codes).
### Stack Overflow
View or ask questions related to Vapor on Stack Overflow using the [`vapor`](http://stackoverflow.com/questions/tagged/vapor) tag.
### GitHub
#### Source Code
To view the framework's source code and code documentation, visit [Vapor's GitHub](https://github.com/vapor/vapor).
#### Issues
To view open bug reports and feature requests, or to create one, visit the [issues](https://github.com/vapor/vapor/issues) tab on [Vapor's GitHub](https://github.com/vapor/vapor).
## Packages
Vapor is a modular framework built for a modular language. Code is split up into modules which are grouped to form packages. Packages can be added to your project by adding the package's Git url to your `Package.swift` file. Once a package is included, all of its modules will be available to `import`. You can read more about packages and modules in the Swift Package Manager [conceptual overview](https://swift.org/package-manager/).
Below is a list of packages and modules that come with or can be used by Vapor projects. Packages will have a link to their respective GitHub page.
### Core
Core packages are maintained by the Vapor team.
#### Included
The following packages are included with Vapor by default.
!!! tip
These packages can also be used individually
- [Vapor](https://github.com/vapor/vapor): Swift's most used web framework.
- Auth: User authentication and persistance.
- Sessions: Secure, ephemeral cookie based data storage.
- Cookies: HTTP cookies.
- Routing: Advanced router with type-safe parameterization.
- [Engine](https://github.com/vapor/engine): Core transport layers.
- HTTP: HTTP client and server.
- URI: URI parsing and serializing.
- WebSockets: Full-duplex communication channels over a single TCP connection.
- SMTP: Send email using Sendgrid and Gmail.
- [Multipart](https://github.com/vapor/multipart): Fast, streaming, non-blocking multipart parser and serializer.
- Multipart: Parses and serializes `multipart/mixed`.
- FormData: Parses and serializes `multipart/form-data`.
- [JSON](https://github.com/vapor/json): Conveniences for working with JSON in Swift.
- [Console](https://github.com/vapor/console): Swift wrapper for console IO and commands.
- [TLS](https://github.com/vapor/tls): Swift wrapper for CLibreSSL's new TLS.
- [Crypto](https://github.com/vapor/crypto): Cryptography from LibreSSL and Swift.
- Digests: Hashing with and without authentication.
- Ciphers: Encryption and decryption
- Random: Pseudo and cryptographically secure randomness.
- BCrypt: Pure Swift implementation.
- [Node](https://github.com/vapor/node): Data structure for easy type conversions.
- [Polymorphic](https://github.com/vapor/polymorphic): Syntax for easily accessing values from common types like JSON.
- [Path Indexable](https://github.com/vapor/path-indexable): A protocol for powerful subscript access of common types like JSON.
- [Core](https://github.com/vapor/core): Core extensions, type-aliases, and functions that facilitate common tasks.
- [Socks](https://github.com/vapor/socks): Swift C Socket API wrapper.
- [Bits](https://github.com/vapor/bits): Low level byte manipulation helpers
#### Providers
These are officially supported packages for Vapor that are not included by default.
- [Fluent](https://github.com/vapor/fluent): Models, relationships, and querying for NoSQL and SQL databases.
- [Fluent Provider](https://github.com/vapor/fluent-provider): Fluent provider for Vapor.
- [MySQL](https://github.com/vapor/mysql): Robust MySQL interface for Swift.
- [MySQL Driver](https://github.com/vapor/mysql-driver): MySQL driver for Fluent.
- [MySQL Provider](https://github.com/vapor/mysql-provider): MySQL provider for Vapor.
- [Leaf](https://github.com/vapor/leaf): An extensible templating language.
- [Leaf Provider](https://github.com/vapor/leaf-provider): Leaf provider for Vapor.
- [Redis](https://github.com/vapor/redbird): Pure-Swift Redis client implemented from the original protocol spec.
- [Redis Provider](https://github.com/vapor/redis-provider): Redis cache provider for Vapor.
- [JWT](https://github.com/vapor/jwt): JSON Web Tokens in Swift.
- [JWT Provider](https://github.com/vapor/jwt-provider): JWT conveniences for Vapor.
### Community
These are packages maintained by community members that work great with Vapor.
- [PostgreSQL](https://github.com/vapor/postgresql): Robust PostgreSQL interface for Swift.
- [PostgreSQL Driver](https://github.com/vapor/postgresql-driver): PostgreSQL driver for Fluent.
- [PostgreSQL Provider](https://github.com/vapor/postgresql-provider): PostgreSQL provider for Vapor.
- [MongoKitten](https://github.com/OpenKitten/MongoKitten): Native MongoDB driver for Swift, written in Swift
- [Mongo Driver](https://github.com/vapor/mongo-driver): MongoKitten driver for Fluent.
- [Mongo Provider](https://github.com/vapor/mongo-provider): MongoKitten provider for Vapor.
- [Kitura Provider](https://github.com/vapor/kitura-provider): Use IBM's Kitura HTTP server in Vapor.
- [SwiftyBeaver](https://github.com/vapor-community/swiftybeaver-provider): Adds the powerful logging of SwiftyBeaver to Vapor.
- [APNS](https://github.com/matthijs2704/vapor-apns): Simple APNS Library for Vapor (Swift).
- [VaporFCM](https://github.com/mdab121/vapor-fcm): Simple FCM (iOS + Android Push Notifications) library built for Vapor in Swift.
- [VaporS3Signer](https://github.com/JustinM1/VaporS3Signer): Generate V4 Auth Header/Pre-Signed URL for AWS S3 REST API
- [Flock](https://github.com/jakeheis/Flock): Automated deployment of Swift projects to servers
- [VaporFlock](https://github.com/jakeheis/VaporFlock): Use Flock to deploy Vapor applications
- [VaporForms](https://github.com/bygri/vapor-forms): Brings simple, dynamic and re-usable web form handling to Vapor.
- [Jobs](https://github.com/BrettRToomey/Jobs): A minimalistic job/background-task system for Swift.
- [Heimdall](https://github.com/himani93/heimdall): An easy to use HTTP request logger.
- [SteamPress](https://github.com/brokenhandsio/SteamPress): A blogging engine for Vapor.
- [Vapor Security Headers](https://github.com/brokenhandsio/VaporSecurityHeaders): Add common security headers to your Vapor Application.
- [MarkdownProvider](https://github.com/vapor-community/markdown-provider) - Easily use Markdown from Leaf.
- [Vapor OAuth](https://github.com/brokenhandsio/vapor-oauth) - An OAuth2 Provider library for Vapor
### Providers
Vapor providers are a convenient way to add functionality to your Vapor projects. For a full list of providers, check out the [`vapor-provider`](https://github.com/search?utf8=✓&q=topic%3Avapor-provider&type=Repositories) tag on GitHub.
## Authors
[Tanner Nelson](mailto:tanner@vapor.codes), [Logan Wright](mailto:logan@vapor.codes), and the hundreds of members of Vapor.

34
3.0/mkdocs.yml Normal file
View File

@ -0,0 +1,34 @@
site_name: 'Vapor Docs'
copyright: 'Copyright &copy; 2017 Qutheory, LLC'
pages:
- Overview: 'index.md'
markdown_extensions:
- admonition
- codehilite(guess_lang=false)
- footnotes
- meta
- toc(permalink=true)
theme: 'material'
theme_dir: 'vapor-material'
repo_url: https://github.com/vapor/documentation
extra_css:
- 'stylesheets/extra.css'
edit_uri: edit/master/2.0/docs/
extra:
logo: 'images/droplet-white.svg'
social:
- type: 'twitter'
link: 'https://twitter.com/@codevapor'
- type: 'slack'
link: 'http://vapor.team/'
font:
text: 'Roboto Slab'
code: 'Source Code Pro'
google_analytics:
- 'UA-76177358-4'
- 'auto'

View File

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block content %}
<h1>404 - Not found</h1>
{% endblock %}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="352" height="448" viewBox="0 0 352 448" id="bitbucket"><path fill="currentColor" d="M203.75 214.75q2 15.75-12.625 25.25t-27.875 1.5q-9.75-4.25-13.375-14.5t-.125-20.5 13-14.5q9-4.5 18.125-3t16 8.875 6.875 16.875zm27.75-5.25q-3.5-26.75-28.25-41T154 165.25q-15.75 7-25.125 22.125t-8.625 32.375q1 22.75 19.375 38.75t41.375 14q22.75-2 38-21t12.5-42zM291.25 74q-5-6.75-14-11.125t-14.5-5.5T245 54.25q-72.75-11.75-141.5.5-10.75 1.75-16.5 3t-13.75 5.5T60.75 74q7.5 7 19 11.375t18.375 5.5T120 93.75Q177 101 232 94q15.75-2 22.375-3t18.125-5.375T291.25 74zm14.25 258.75q-2 6.5-3.875 19.125t-3.5 21-7.125 17.5-14.5 14.125q-21.5 12-47.375 17.875t-50.5 5.5-50.375-4.625q-11.5-2-20.375-4.5T88.75 412 70.5 401.125t-13-15.375q-6.25-24-14.25-73l1.5-4 4.5-2.25q55.75 37 126.625 37t126.875-37q5.25 1.5 6 5.75t-1.25 11.25-2 9.25zM350.75 92.5q-6.5 41.75-27.75 163.75-1.25 7.5-6.75 14t-10.875 10T291.75 288q-63 31.5-152.5 22-62-6.75-98.5-34.75-3.75-3-6.375-6.625t-4.25-8.75-2.25-8.5-1.5-9.875T25 232.75q-2.25-12.5-6.625-37.5t-7-40.375T5.5 118 0 78.5Q.75 72 4.375 66.375T12.25 57t11.25-7.5T35 43.875t12-4.625q31.25-11.5 78.25-16 94.75-9.25 169 12.5Q333 47.25 348 66.25q4 5 4.125 12.75t-1.375 13.5z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" viewBox="0 0 416 448" id="github"><path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19T128 352t-18.125-8.5-10.75-19T96 304t3.125-20.5 10.75-19T128 256t18.125 8.5 10.75 19T160 304zm160 0q0 10-3.125 20.5t-10.75 19T288 352t-18.125-8.5-10.75-19T256 304t3.125-20.5 10.75-19T288 256t18.125 8.5 10.75 19T320 304zm40 0q0-30-17.25-51T296 232q-10.25 0-48.75 5.25Q229.5 240 208 240t-39.25-2.75Q130.75 232 120 232q-29.5 0-46.75 21T56 304q0 22 8 38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 37.25-1.75t35-7.375 30.5-15 20.25-25.75T360 304zm56-44q0 51.75-15.25 82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5T212 416q-19.5 0-35.5-.75t-36.875-3.125-38.125-7.5-34.25-12.875T37 371.5t-21.5-28.75Q0 312 0 260q0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 30.875Q171.5 96 212 96q37 0 70 8 26.25-20.5 46.75-30.25T376 64q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 99.5z"/></svg>

After

Width:  |  Height:  |  Size: 991 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 500 500" id="gitlab"><path fill="currentColor" d="M93.667 473.347l90.684-279.097H2.983l90.684 279.097z" transform="translate(156.198 1.16)"/><path fill="currentColor" d="M221.333 473.345L130.649 194.25H3.557l217.776 279.095z" transform="translate(28.531 1.16)" opacity=".7"/><path fill="currentColor" d="M32 195.155L4.441 279.97a18.773 18.773 0 0 0 6.821 20.99l238.514 173.29L32 195.155z" transform="translate(.089 .256)" opacity=".5"/><path fill="currentColor" d="M2.667-84.844h127.092L75.14-252.942c-2.811-8.649-15.047-8.649-17.856 0L2.667-84.844z" transform="translate(29.422 280.256)"/><path fill="currentColor" d="M2.667 473.345L93.351 194.25h127.092L2.667 473.345z" transform="translate(247.198 1.16)" opacity=".7"/><path fill="currentColor" d="M221.334 195.155l27.559 84.815a18.772 18.772 0 0 1-6.821 20.99L3.557 474.25l217.777-279.095z" transform="translate(246.307 .256)" opacity=".5"/><path fill="currentColor" d="M130.667-84.844H3.575l54.618-168.098c2.811-8.649 15.047-8.649 17.856 0l54.618 168.098z" transform="translate(336.974 280.256)"/></svg>

After

Width:  |  Height:  |  Size: 1.1 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

View File

@ -0,0 +1,158 @@
{% import "partials/language.html" as lang %}
<!DOCTYPE html>
<html class="no-js">
<head>
{% block site_meta %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
{% if config.site_description %}
<meta name="description" content="{{ config.site_description }}">
{% endif %}
{% if page.canonical_url %}
<link rel="canonical" href="{{ page.canonical_url }}">
{% endif %}
{% if config.site_author %}
<meta name="author" content="{{ config.site_author }}">
{% endif %}
{% 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.png">
{% endif %}
<meta name="generator" content="mkdocs-{{ mkdocs_version }}, mkdocs-material-1.1.1">
{% endblock %}
{% block htmltitle %}
{% if page.title and not page.is_homepage %}
<title>{{ page.title }} - {{ config.site_name }}</title>
{% else %}
<title>{{ config.site_name }}</title>
{% endif %}
{% endblock %}
{% block libs %}
<script src="{{ base_url }}/assets/javascripts/modernizr-56ade86843.js"></script>
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-e5b48ab351.css">
{% if config.extra.palette %}
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-892b79c5c5.palette.css">
{% endif %}
{% endblock %}
{% block fonts %}
{% if config.extra.font != "none" %}
{% set text = config.extra.get("font", {}).text | default("Roboto") %}
{% set code = config.extra.get("font", {}).code
| default("Roboto Mono") %}
{% set font = text + ':300,400,400i,700|' + code | replace(' ', '+') %}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family={{ font }}">
<style>body,input{font-family:"{{ text }}","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"{{ code }}","Courier New",Courier,monospace}</style>
{% endif %}
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
{% endblock %}
{% for path in extra_css %}
<link rel="stylesheet" href="{{ path }}">
{% endfor %}
{% block extrahead %}{% endblock %}
</head>
{% set palette = config.extra.get("palette", {}) %}
{% set primary = palette.primary | replace(" ", "-") | lower %}
{% set accent = palette.accent | replace(" ", "-") | lower %}
{% if primary or accent %}
<body data-md-color-primary="{{ primary }}" data-md-color-accent="{{ accent }}">
{% else %}
<body>
{% endif %}
<svg class="md-svg">
<defs>
{% set platform = config.extra.repo_icon or config.repo_url %}
{% if "github" in platform %}
{% include "assets/images/icons/github-1da075986e.svg" %}
{% elif "gitlab" in platform %}
{% include "assets/images/icons/gitlab-5ad3f9f9e5.svg" %}
{% elif "bitbucket" in platform %}
{% include "assets/images/icons/bitbucket-670608a71a.svg" %}
{% endif %}
</defs>
</svg>
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="drawer">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="search">
<label class="md-overlay" data-md-component="overlay" for="drawer"></label>
{% block header %}
{% 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 %}
{% if nav %}
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/nav.html" %}
</div>
</div>
</div>
{% endif %}
{% if page.toc %}
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/toc.html" %}
</div>
</div>
</div>
{% endif %}
{% endblock %}
<div class="md-content">
<article class="md-content__inner md-typeset">
{% if config.edit_uri %}
<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" 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>
</main>
{% block footer %}
{% include "partials/footer.html" %}
{% endblock %}
</div>
{% block scripts %}
<script src="{{ base_url }}/assets/javascripts/application-f6cd941c96.js"></script>
<script>app.initialize({url:{base:"{{ base_url }}"}})</script>
{% for path in extra_javascript %}
<script src="{{ path }}"></script>
{% endfor %}
{% endblock %}
{% block analytics %}
{% if config.google_analytics %}
<script>!function(e,t,n,a,o,c,i){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*new Date,c=t.createElement(n),i=t.getElementsByTagName(n)[0],c.async=1,c.src=a,i.parentNode.insertBefore(c,i)}(window,document,"script","https://www.google-analytics.com/analytics.js","ga"),ga("create","{{ config.google_analytics[0] }}","{{ config.google_analytics[1] }}"),ga("set","anonymizeIp",!0),ga("send","pageview");var links=document.getElementsByTagName("a");Array.prototype.map.call(links,function(e){e.host!=document.location.host&&e.addEventListener("click",function(){var t=e.getAttribute("data-md-action")||"follow";ga("send","event","outbound",t,e.href)})}),document.forms.search.query.addEventListener("blur",function(){if(this.value){var e=document.location.pathname;ga("send","pageview",e+"?q="+this.value)}})</script>
{% endif %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1 @@
{% extends "base.html" %}

View File

@ -0,0 +1,58 @@
{% import "partials/language.html" as lang %}
<footer class="md-footer">
{% if page.previous_page or page.next_page %}
<div class="md-footer-nav">
<nav class="md-footer-nav__inner md-grid">
{% if page.previous_page %}
<a href="{{ page.previous_page.url }}" title="{{ page.previous_page.title }}" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
</div>
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
{{ lang.t('footer.previous') }}
</span>
{{ page.previous_page.title }}
</span>
</div>
</a>
{% endif %}
{% if page.next_page %}
<a href="{{ page.next_page.url }}" title="{{ page.next_page.title }}" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
{{ lang.t('footer.next') }}
</span>
{{ page.next_page.title }}
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
</div>
</a>
{% endif %}
</nav>
</div>
{% endif %}
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-copyright">
{% if config.copyright %}
<div class="md-footer-copyright__highlight">
{{ config.copyright }}
</div>
{% endif %}
powered by
<a href="http://www.mkdocs.org" title="MkDocs">MkDocs</a>
and
<a href="http://squidfunk.github.io/mkdocs-material/" title="Material for MkDocs">
Material for MkDocs</a>
</div>
{% block social %}
{% include "partials/social.html" %}
{% endblock %}
</div>
</div>
</footer>

View File

@ -0,0 +1,46 @@
<header class="md-header">
<nav class="md-header-nav md-grid">
<div class="md-flex">
<div class="md-flex__cell md-flex__cell--shrink">
{% 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">
</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>
</div>
<div class="md-flex__cell md-flex__cell--stretch">
<span class="md-flex__ellipsis md-header-nav__title">
{% block site_name %}
{% if page %}
{% for parent in page.ancestors %}
<span class="md-header-nav__parent">
{{ parent.title }}
</span>
{% endfor %}
{% endif %}
{{ page.title | default(config.site_name, true) }}
{% endblock %}
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
{% block search_box %}
<label class="md-icon md-icon--search md-header-nav__button" for="search"></label>
{% include "partials/search.html" %}
{% 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>
</div>
</nav>
</header>

View File

@ -0,0 +1,9 @@
{% macro t(key) %}{{ {
"edit.link.title": "Edit this page",
"comments": "Comments",
"footer.previous": "Previous",
"footer.next": "Next",
"search.placeholder": "Search",
"source.link.title": "Go to repository",
"toc.title": "Table of contents"
}[key] }}{% endmacro %}

View File

@ -0,0 +1,54 @@
{% set class = "md-nav__item" %}
{% if nav_item.active %}
{% set class = "md-nav__item md-nav__item--active" %}
{% endif %}
{% if nav_item.children %}
<li class="{{ class }} md-nav__item--nested">
{% if nav_item.active %}
<input class="md-toggle md-nav__toggle" data-md-toggle="{{ path }}" type="checkbox" id="{{ path }}" checked>
{% else %}
<input class="md-toggle md-nav__toggle" data-md-toggle="{{ path }}" type="checkbox" id="{{ path }}">
{% endif %}
<label class="md-nav__link" for="{{ path }}">
{{ nav_item.title }}
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="{{ level }}">
<label class="md-nav__title" for="{{ path }}">
{{ 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>
</nav>
</li>
{% elif nav_item == page %}
<li class="{{ class }}">
{% set toc_ = page.toc %}
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc">
{% if toc_ | first is defined %}
{% set toc_ = (toc_ | first).children %}
{% endif %}
{% if toc_ | first is defined %}
<label class="md-nav__link md-nav__link--active" for="toc">
{{ nav_item.title }}
</label>
{% endif %}
<a href="{{ nav_item.url }}" title="{{ nav_item.title }}" class="md-nav__link md-nav__link--active">
{{ nav_item.title }}
</a>
{% if toc_ | first is defined %}
{% include "partials/toc.html" %}
{% endif %}
</li>
{% else %}
<li class="{{ class }}">
<a href="{{ nav_item.url }}" title="{{ nav_item.title }}" class="md-nav__link">
{{ nav_item.title }}
</a>
</li>
{% endif %}

View File

@ -0,0 +1,24 @@
<nav class="md-nav md-nav--primary" data-md-level="0">
<label class="md-nav__title md-nav__title--site" for="drawer">
{% if config.extra.logo %}
<i class="md-logo md-nav__button">
<img src="{{ base_url }}/{{ config.extra.logo }}">
</i>
{% else %}
<i class="md-icon md-icon--home md-nav__button"></i>
{% endif %}
{{ config.site_name }}
</label>
{% if config.repo_url %}
<div class="md-nav__source">
{% include "partials/source.html" %}
</div>
{% endif %}
<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>
</nav>

View File

@ -0,0 +1,15 @@
{% import "partials/language.html" as lang %}
<div class="md-search" data-md-component="search">
<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" data-md-component="query">
<label class="md-icon md-search__icon" for="search"></label>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="result"></div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,9 @@
{% if config.extra.social %}
<div class="md-footer-social">
{% set path = "ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/{{ path }}">
{% for social in config.extra.social %}
<a href="{{ social.link }}" class="md-footer-social__link fa fa-{{ social.type }}"></a>
{% endfor %}
</div>
{% endif %}

View File

@ -0,0 +1,25 @@
{% import "partials/language.html" as lang %}
{% set platform = config.extra.repo_icon or config.repo_url %}
{% if "github" in platform %}
{% set repo_type = "github" %}
{% elif "gitlab" in platform %}
{% set repo_type = "gitlab" %}
{% elif "bitbucket" in platform %}
{% set repo_type = "bitbucket" %}
{% else %}
{% set repo_type = "" %}
{% endif %}
{% block repo %}
<a href="{{ config.repo_url }}" title="{{ lang.t('source.link.title') }}" class="md-source" data-md-source="{{ repo_type }}">
{% if repo_type %}
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#{{ repo_type }}" width="24" height="24"></use>
</svg>
</div>
{% endif %}
<div class="md-source__repository">
{{ config.repo_name }}
</div>
</a>
{% endblock %}

View File

@ -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 %}

View File

@ -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>

View File

@ -0,0 +1,14 @@
<li class="md-nav__item">
<a href="{{ toc_item.url }}" title="{{ toc_item.title }}" class="md-nav__link">
{{ toc_item.title }}
</a>
{% if toc_item.children %}
<nav class="md-nav">
<ul class="md-nav__list">
{% for toc_item in toc_item.children %}
{% include "partials/toc-item.html" %}
{% endfor %}
</ul>
</nav>
{% endif %}
</li>

View File

@ -0,0 +1,15 @@
{% import "partials/language.html" as lang %}
<nav class="md-nav md-nav--secondary">
{% set toc_ = page.toc %}
{% if toc_ | first is defined and "\x3ch1 id=" in page.content %}
{% set toc_ = (toc_ | first).children %}
{% endif %}
{% 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_ %}
{% include "partials/toc-item.html" %}
{% endfor %}
</ul>
{% endif %}
</nav>