all whitespace is dislayed as is by the browser, and + // sometimes, Prism adds a newline before the code) + if (html.startsWith('\n')) { + html = html.replace('\n', ''); + } + + return ( +++ ); + } +} + +export const InlineCode = ({ children }) => ( ++ + +
+ {children} + +
+); diff --git a/docs/components/base/generate-slug.js b/docs/components/base/generate-slug.js new file mode 100644 index 00000000000000..2aaab6c771af66 --- /dev/null +++ b/docs/components/base/generate-slug.js @@ -0,0 +1,20 @@ +const slugs = require(`github-slugger`)(); +slugs.reset(); + +const generateSlug = node => { + return slugs.slug(toString(node)); +}; + +const toString = node => { + if (typeof node === 'string') { + return node; + } else if (Array.isArray(node)) { + return node.map(toString).join(''); + } else if (node.props.children) { + return toString(node.props.children); + } else { + return ''; + } +}; + +export default generateSlug; diff --git a/docs/components/base/head.js b/docs/components/base/head.js new file mode 100644 index 00000000000000..b64086d6b98efc --- /dev/null +++ b/docs/components/base/head.js @@ -0,0 +1,34 @@ +import React from 'react'; +import NextHead from 'next/head'; + +class Head extends React.PureComponent { + render() { + return ( +++ ); + } +} + +export default Head; diff --git a/docs/components/base/headers.js b/docs/components/base/headers.js new file mode 100644 index 00000000000000..d5dfd5e2af037d --- /dev/null +++ b/docs/components/base/headers.js @@ -0,0 +1,82 @@ +import React from 'react'; +import * as Constants from '~/style/constants'; + +export const H1 = ({ children }) => ( ++ +{this.props.title} + + + + + + + + + + + + + + + + + + {this.props.children} ++ {children} + +
+); + +export const H2 = ({ children }) => ( ++ {children} + +
+); + +export const H3 = ({ children }) => ( ++ {children} + + +
+); + +export const H4 = ({ children }) => ( ++ {children} + +
+); diff --git a/docs/components/base/link.js b/docs/components/base/link.js new file mode 100644 index 00000000000000..22405326efba5b --- /dev/null +++ b/docs/components/base/link.js @@ -0,0 +1,45 @@ +import React from 'react'; +import NativeLink from 'next/link'; +import * as Constants from '~/style/constants'; + +export const InternalLink = ({ href, as, children }) => ( ++ + {children} + + + + +); + +export const ExternalLink = ({ href, children }) => ( + + {children} + + + +); diff --git a/docs/components/base/list.js b/docs/components/base/list.js new file mode 100644 index 00000000000000..80428c38bc088a --- /dev/null +++ b/docs/components/base/list.js @@ -0,0 +1,79 @@ +import React from 'react'; +import PermalinkIcon from '~/components/icons/permalink-icon'; +import BulletIcon from '~/components/icons/bullet-icon'; +import generateSlug from '~/components/base/generate-slug'; + +export const UL = ({ children }) => ( +
{children}
+ ++ {children} + ++); diff --git a/docs/components/base/permalink.js b/docs/components/base/permalink.js new file mode 100644 index 00000000000000..f5cb291c424c3c --- /dev/null +++ b/docs/components/base/permalink.js @@ -0,0 +1,84 @@ +import React from 'react'; +import PermalinkIcon from '~/components/icons/permalink-icon'; +import * as Constants from '~/style/constants'; +import generateSlug from '~/components/base/generate-slug'; + +class Permalink extends React.Component { + render() { + const { component, className, children, ...rest } = this.props; + return React.cloneElement( + component, + { + className: [className, component.props.className || ''].join(' '), + ...rest, + }, + children + ); + } +} + +const fn = props => { + const component = props.children; + const children = component.props.children || ''; + let id = props.id; + + if (id == null) { + id = generateSlug(children); + } + + return ( +
components respectively
+ let codeBlocks = 0;
+ let inlineCodeBlocks = 0;
+ markdown = markdown.replace(/(`{1,3})/gi, (match, quotes) => {
+ if (quotes === '```') {
+ // Replace ``` with and
+ if (codeBlocks % 2 === 0) {
+ codeBlocks++;
+ return '${({`';
+ } else {
+ codeBlocks++;
+ return '`}
)}';
+ }
+ } else {
+ // If we are inside a block, return \`, not
+ if (codeBlocks % 2 === 1) {
+ return '\\`';
+ }
+ if (inlineCodeBlocks % 2 === 0) {
+ inlineCodeBlocks++;
+ return '${({`';
+ } else {
+ inlineCodeBlocks++;
+ return '`} )}';
+ }
+ }
+ });
+
+ // Replace $ with \$ (i.e. escape $) inside and blocks
+ let regex = /{`([\s\S]*?)`}<\/Code>/g;
+ markdown = markdown.replace(regex, match => {
+ return match.replace(/\$/gi, '\\$');
+ });
+
+ regex = /{`([\s\S]*?)`}<\/InlineCode>/g;
+ markdown = markdown.replace(regex, match => {
+ return match.replace(/\$/gi, '\\$');
+ });
+
+ // Extract language marker from ``` blocks and turn it into a prop on
+ regex = /{`(\S*)/g;
+ markdown = markdown.replace(regex, (match, lang) => {
+ return `{\``;
+ });
+
+ // Replace URLs for images
+ regex = /!\[.*\]\((.*)\.(gif|png|jpg|jpeg|svg)/g;
+ markdown = markdown.replace(regex, match => {
+ return match.replace('./', `/static/images/generated/${filePath}/`);
+ });
+
+ // Linkify URLs
+ regex = /(\s)(https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))\b/g;
+ markdown = markdown.replace(regex, (match, leadingSpace, url) => {
+ return leadingSpace + `[${url}](${url})`;
+ });
+
+ let output = `
+ import React from 'react';
+ import markdown from 'markdown-in-js';
+ import withDoc, { components } from '~/lib/with-doc';
+ import { Code, InlineCode } from '~/components/base/code';
+ import SnackEmbed from '~/components/plugins/snack-embed';
+ export default withDoc({
+ title: '${frontmatter.title}',
+ })(markdown(components)\`
+ ${markdown}
+ \`);
+ `;
+
+ // Run prettier over the code
+ const options = prettier.resolveConfig.sync('../.prettierrc');
+ output = prettier.format(output, options);
+
+ // Create directory if it doesn't exist
+ fs.ensureDirSync(`${DESTINATION_PATH_PREFIX}/${filePath}`);
+ fs.writeFileSync(`${DESTINATION_PATH_PREFIX}/${filePath}/${filename}.js`, output);
+};
+
+module.exports = generateJsPage;
diff --git a/docs/mdjs/index.js b/docs/mdjs/index.js
new file mode 100644
index 00000000000000..09229377c484f8
--- /dev/null
+++ b/docs/mdjs/index.js
@@ -0,0 +1,87 @@
+const path = require('path');
+const fs = require('fs-extra');
+const jsonfile = require('jsonfile');
+
+const generatePage = require('./generatePage');
+const generateNavLinks = require('./generateNav');
+
+const ORIGINAL_PATH_PREFIX = './versions';
+const DESTINATION_PATH_PREFIX = './pages/versions';
+
+const generateJsFromMd = path_ => {
+ let items = fs.readdirSync(path_);
+
+ for (var i = 0; i < items.length; i++) {
+ const filePath = path.join(path_, items[i]);
+ if (fs.statSync(filePath).isDirectory()) {
+ generateJsFromMd(filePath);
+ } else {
+ const { ext, name } = path.parse(filePath);
+ if (ext === '.md') {
+ const relativePath = path
+ .resolve(filePath)
+ .replace(path.resolve(ORIGINAL_PATH_PREFIX) + '/', '');
+ generatePage(path.dirname(relativePath), name);
+ } else {
+ const relativePath = path
+ .resolve(filePath)
+ .replace(path.resolve(ORIGINAL_PATH_PREFIX) + '/', '');
+ fs.ensureDirSync(`./static/images/generated`);
+ fs.copySync(filePath, `./static/images/generated/${relativePath}`);
+ }
+ }
+ }
+};
+
+let versions = fs.readdirSync(ORIGINAL_PATH_PREFIX);
+
+// Compile all files initially
+
+console.time('initial compile');
+
+const navigationData = [];
+
+versions.forEach(dir => {
+ if (!fs.lstatSync(`${ORIGINAL_PATH_PREFIX}/${dir}`).isDirectory()) return;
+ const version = dir === 'unversioned' ? 'unversioned' : dir.replace('.0.0', '');
+
+ fs.emptyDirSync(`${DESTINATION_PATH_PREFIX}/${dir}`);
+
+ console.log(`Processing markdown files in ${dir}`);
+ generateJsFromMd(`${ORIGINAL_PATH_PREFIX}/${dir}`);
+
+ navigationData.push({
+ version,
+ navigation: generateNavLinks(`${ORIGINAL_PATH_PREFIX}/${dir}`, []),
+ });
+});
+
+console.log(`Generating navigation JSON, writing to ./navigation-data.json`);
+jsonfile.writeFileSync(`./navigation-data.json`, navigationData);
+
+console.log(`Create an index page under pages/version`);
+fs.writeFileSync(
+ `${DESTINATION_PATH_PREFIX}/index.js`,
+ `
+import redirect from '~/lib/redirect';
+export default redirect('/versions/latest');
+`
+);
+
+console.timeEnd('initial compile');
+
+// Watch for changes in directory
+
+if (process.argv.length < 3) {
+ fs.watch(`${ORIGINAL_PATH_PREFIX}`, { recursive: true }, (eventType, filename) => {
+ let filePath = path.join(`${ORIGINAL_PATH_PREFIX}`, filename);
+ const { ext, name } = path.parse(filePath);
+ if (ext === '.md') {
+ const relativePath = path
+ .resolve(filePath)
+ .replace(path.resolve(ORIGINAL_PATH_PREFIX) + '/', '');
+ console.log(`Processing changes for ${filePath} | ${path.dirname(relativePath)} | ${name}`);
+ generatePage(path.dirname(relativePath), name);
+ }
+ });
+}
diff --git a/docs/mdjs/navbarOrder.js b/docs/mdjs/navbarOrder.js
new file mode 100644
index 00000000000000..da2dd3d5a598ee
--- /dev/null
+++ b/docs/mdjs/navbarOrder.js
@@ -0,0 +1,81 @@
+const ROOT = [
+ 'Introduction',
+ 'Working with Expo',
+ 'Guides',
+ 'Distributing Your App',
+ 'Tutorials',
+ 'ExpoKit',
+ 'SDK API Reference',
+];
+
+const INTRODUCTION = [
+ 'Quick Start',
+ 'Installation',
+ 'XDE Tour',
+ 'Project Lifecycle',
+ 'Community',
+ 'Additional Resources',
+ 'Troubleshooting Proxies',
+ 'Frequently Asked Questions',
+ 'Already used React Native?',
+ 'Why not Expo?',
+];
+
+const GUIDES = [
+ 'App Icons',
+ 'Assets',
+ 'Error Handling',
+ 'Preloading & Caching Assets',
+ 'Icons',
+ 'Custom Fonts',
+ 'Splash Screens',
+ 'Using Custom Fonts',
+ 'Routing & Navigation',
+ 'Configuring StatusBar',
+ 'Offline Support',
+ 'Push Notifications',
+ 'Using Modern JavaScript',
+ 'Using ClojureScript',
+ 'Using Firebase',
+ 'Using GraphQL',
+ 'Using Sentry',
+];
+
+const WORKING_WITH_EXPO = [
+ 'Up and Running',
+ 'Glossary of terms',
+ 'Configuration with app.json',
+ 'Development Mode',
+ 'exp Command-Line Interface',
+ 'Viewing Logs',
+ 'Debugging',
+ 'Genymotion',
+ 'Release Channels',
+ 'Building Standalone Apps',
+ 'Publishing',
+ 'Linking',
+ 'Detaching to ExpoKit',
+ 'Developing With ExpoKit',
+ 'Advanced ExpoKit Topics',
+ 'Expo & "Create React Native App"',
+ 'How Expo Works',
+ 'Upgrading Expo',
+];
+
+const DISTRIBUTION = [
+ 'Building Standalone Apps',
+ 'Deploying to App Stores',
+ 'Release Channels',
+ 'Advanced Release Channels',
+];
+
+const EXPOKIT = ['Detaching to ExpoKit', 'Developing With ExpoKit', 'Advanced ExpoKit Topics'];
+
+module.exports = {
+ ROOT,
+ INTRODUCTION,
+ WORKING_WITH_EXPO,
+ GUIDES,
+ DISTRIBUTION,
+ EXPOKIT,
+};
diff --git a/docs/package.json b/docs/package.json
new file mode 100644
index 00000000000000..57137128357a63
--- /dev/null
+++ b/docs/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "expo-docs",
+ "version": "25.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "concurrently -k 'yarn run mdjs' 'node server.js'",
+ "build": "yarn run mdjs-prod && cross-env NODE_ENV=production next build",
+ "export": "cross-env NODE_ENV=production next export",
+ "start": "cross-env NODE_ENV=production node server.js",
+ "lint": "eslint .",
+ "lint:staged": "lint-staged",
+ "test": "npm run lint",
+ "mdjs": "babel-node ./mdjs",
+ "mdjs-prod": "node ./mdjs --no-watch",
+ "fix-markdown": "node ./transition/fix-markdown.js",
+ "clean": "rm -r docs pages/versions navigation-data.json static/images/generated",
+ "update-search-index": "node ./scripts/update-search-index"
+ },
+ "dependencies": {
+ "babel-plugin-transform-define": "1.3.0",
+ "babel-runtime": "6.23.0",
+ "cross-env": "5.0.1",
+ "docsearch.js": "^2.4.1",
+ "front-matter": "^2.3.0",
+ "fs-extra": "^4.0.2",
+ "github-slugger": "^1.2.0",
+ "glob-promise": "^3.1.0",
+ "hotshot": "^1.0.5",
+ "jsonfile": "^4.0.0",
+ "jsonwebtoken": "^7.4.1",
+ "lodash": "^4.17.3",
+ "lodash.debounce": "^4.0.8",
+ "markdown-in-js": "1.1.3",
+ "mitt": "1.1.2",
+ "mkdirp": "^0.5.1",
+ "next": "4.0.0-beta.2",
+ "nprogress": "0.2.0",
+ "prettier": "^1.10.2",
+ "prismjs": "^1.11.0",
+ "prop-types": "15.5.8",
+ "query-string": "4.3.4",
+ "react": "16",
+ "react-dom": "16",
+ "scroll-into-view-if-needed": "1.1.0",
+ "showdown": "^1.8.5",
+ "smoothscroll-polyfill": "0.3.5"
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.0.0-beta.32",
+ "@babel/core": "^7.0.0-beta.32",
+ "babel-plugin-root-import": "^5.1.0",
+ "concurrently": "^3.3.0",
+ "lint-staged": "4.0.0",
+ "shelljs": "^0.7.8"
+ },
+ "lint-staged": {
+ "*.js": [
+ "eslint",
+ "prettier --write --no-semi --single-quote",
+ "git add"
+ ]
+ }
+}
diff --git a/docs/pipeline.js b/docs/pipeline.js
new file mode 100644
index 00000000000000..0f8e76b6474693
--- /dev/null
+++ b/docs/pipeline.js
@@ -0,0 +1,166 @@
+import git from 'git-promise';
+import spawnAsync from '@exponent/spawn-async';
+import CI, { Rocker, Kubernetes as K8S, Log } from 'ci';
+
+export default {
+ config: {
+ name: 'Next Docs',
+ shortname: 'next-docs',
+ description: 'Next Docs',
+ branches: 'master',
+ allowPRs: true,
+ regions: ['docs/**'],
+ },
+ steps: (branch, tag, pr) => {
+ if (tag) {
+ // all we need to do when there's a tag is deploy
+ return [deploy(branch, tag, 'production'), CI.waitStep(), updateSearchIndex(branch, tag, pr)];
+ }
+
+ // For PRs, immediately build but don't deploy
+ let steps = [build(branch, tag, pr)];
+
+ // If this is not a pull request, first, deploy to staging and then, tag release and deploy to production
+ // once that step is unblocked
+ // if (!pr) {
+ if (pr) {
+ steps = [
+ ...steps,
+ CI.waitStep(),
+ deploy(branch, tag, 'staging'),
+ CI.blockStep(':shipit: Deploy to Production?'),
+ tagRelease,
+ ];
+ }
+ return steps;
+ },
+};
+
+const IMAGE_NAME = 'gcr.io/exponentjs/exponent-docs-v2';
+
+const build = (branch, tag, pr) => ({
+ name: ':hammer: Build',
+ agents: {
+ queue: 'builder',
+ },
+ async command() {
+ const imageTag = `${process.env.BUILDKITE_COMMIT}`;
+
+ Log.collapsed(':docker: Building...');
+
+ await Rocker.build({
+ rockerfile: './Rockerfile',
+ context: './',
+ vars: {
+ ImageName: IMAGE_NAME,
+ ImageTag: imageTag,
+ },
+ options: {
+ pull: true,
+ push: true,
+ },
+ });
+ },
+});
+
+const deploy = (branch, tag, environment) => ({
+ name: ':kubernetes: Deploy',
+ agents: {
+ queue: 'builder',
+ },
+ async command() {
+ Log.collapsed(':kubernetes: Deploying...');
+
+ const imageTag = `${process.env.BUILDKITE_COMMIT}`;
+ const replicaCount = environment === 'production' ? 2 : 1;
+ const appVersion = await makeVersionName();
+
+ let ingressHostname;
+ if (environment === 'production') {
+ ingressHostname = 'next-docs.expo.io';
+ } else {
+ ingressHostname = 'staging.next-docs.expo.io';
+ }
+
+ await K8S.deployHelmChart({
+ clusterName: 'exp-central',
+ chartPath: './deploy/charts/docs',
+ namespace: 'next-docs',
+ releaseName: `next-docs-${environment}`,
+ values: {
+ image: {
+ repository: IMAGE_NAME,
+ tag: imageTag,
+ },
+ replicaCount,
+ ingress: [
+ {
+ host: ingressHostname,
+ },
+ ],
+ additionalEnv: [
+ {
+ name: 'GIT_COMMIT',
+ value: process.env.BUILDKITE_COMMIT,
+ },
+ {
+ name: 'APP_VERSION',
+ value: appVersion,
+ },
+ {
+ name: 'RELEASE_ID',
+ value: md5(appVersion),
+ },
+ ],
+ },
+ });
+ },
+});
+
+async function makeVersionName() {
+ const hash = (await git('rev-parse --short=12 HEAD')).trim();
+
+ const pad = n => {
+ return n < 10 ? `0${n}` : `${n}`;
+ };
+
+ const today = new Date();
+ const currentDateStr = `${today.getFullYear()}-${pad(today.getMonth() + 1)}-${pad(
+ today.getDate()
+ )}`;
+ return `${currentDateStr}-${hash}`;
+}
+
+function md5(val) {
+ return require('crypto')
+ .createHash('md5')
+ .update(val)
+ .digest('hex');
+}
+
+const updateSearchIndex = (branch, tag, pr) => ({
+ name: `:feelsgood: Update Search Index`,
+ async command() {
+ if (branch !== 'master' && !tag) {
+ return;
+ }
+
+ Log.collapsed(':open_mouth: Updating search index...');
+
+ await spawnAsync('yarn', ['run', 'update-search-index', '--', 'next-docs.expo.io'], {
+ stdio: 'inherit',
+ });
+ },
+});
+
+const tagRelease = {
+ name: ':git: Tag Release',
+ async command() {
+ Log.collapsed(':git: Tagging Release...'); // Build tag name
+ const tag = `next-docs/release-${await makeVersionName()}`;
+ await git(`tag ${tag}`);
+ Log.collapsed(':github: Pushing Release...');
+ await git(`push origin ${tag}`); // upload more steps
+ await global.currentPipeline.upload(await global.currentPipeline.steps(tag, tag, null));
+ },
+};
diff --git a/docs/scripts/update-search-index.js b/docs/scripts/update-search-index.js
new file mode 100644
index 00000000000000..82c602141b00e5
--- /dev/null
+++ b/docs/scripts/update-search-index.js
@@ -0,0 +1,98 @@
+const spawn = require('child_process').spawn;
+const fs = require('fs');
+const path = require('path');
+
+const VERSIONS_DIR = path.join(__dirname, '..', 'versions');
+const args = process.argv.slice(2);
+const DOCS_URL = `https://${args[0]}/versions`;
+const ALGOLIA_APP_ID = 'S6DBW4862L';
+const ALGOLIA_API_KEY = '06b11271d5c32d6670c9bcbb73e95e85';
+
+console.log(`Indexing docs at URL: ${DOCS_URL}`);
+
+function buildAlgoliaConfig() {
+ const baseAlgoliaConfig = {
+ index_name: 'exponent-docs-v3',
+ start_urls: [],
+ stop_urls: [],
+ selectors_exclude: ['a.anchor'],
+ selectors: {
+ lvl0: {
+ selector: '.sidebar a.selected',
+ default_value: 'Documentation',
+ global: true,
+ },
+ lvl1: '.doc-layout h1',
+ lvl2: '.doc-markdown h2',
+ lvl3: '.doc-markdown h3',
+ lvl4: '.doc-markdown h4',
+ text: '.doc-markdown div, .doc-markdown li, .doc-markdown dl, .doc-markdown pre',
+ },
+ min_indexed_level: 1,
+ scrap_start_urls: false,
+ nb_hits: 63146,
+ };
+
+ const versionDirs = getDirectories(VERSIONS_DIR);
+ const algoliaConfig = baseAlgoliaConfig;
+ versionDirs.forEach(version => {
+ const versionUrlBase = `${DOCS_URL}/${version}`;
+ algoliaConfig.start_urls.push(
+ ...[
+ {
+ url: `${versionUrlBase}/`,
+ tags: [version],
+ },
+ {
+ url: `${versionUrlBase}/index.html`,
+ tags: [version],
+ },
+ ]
+ );
+
+ const sections = getDirectories(path.join(VERSIONS_DIR, version));
+ algoliaConfig.stop_urls = [
+ ...algoliaConfig.stop_urls,
+ ...sections.map(s => `${versionUrlBase}/${s}/index.html$`),
+ ];
+ });
+
+ return algoliaConfig;
+}
+
+function getDirectories(path) {
+ return fs.readdirSync(path).filter(function(file) {
+ return fs.statSync(path + '/' + file).isDirectory();
+ });
+}
+
+function escape(str) {
+ return str.replace(/\"/g, '\\"');
+}
+
+// Do work
+const config = buildAlgoliaConfig();
+console.log(config);
+
+spawn(
+ 'docker',
+ [
+ 'run',
+ '--rm',
+ '-e',
+ `APPLICATION_ID=${ALGOLIA_APP_ID}`,
+ '-e',
+ `API_KEY=${ALGOLIA_API_KEY}`,
+ '-e',
+ `CONFIG='${JSON.stringify(config)}'`,
+ '--name',
+ 'exponent-doc-builder',
+ '-t',
+ 'gcr.io/exponentjs/algolia-documentation-scraper:latest',
+ '/root/run',
+ ],
+ {
+ stdio: 'inherit',
+ shell: true,
+ }
+);
diff --git a/docs/scripts/versionpatch.sh b/docs/scripts/versionpatch.sh
new file mode 100644
index 00000000000000..3bc27717a536ec
--- /dev/null
+++ b/docs/scripts/versionpatch.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+if [[ $# -lt 2 ]]
+then
+ echo "applies the current git diff in source version in each target version"
+ echo "can be used to, for example, make changes in "'unversioned'" then"
+ echo "duplicate those changes in previous versions"
+ echo
+ echo "usage: from docs directory,"
+ echo " ./scripts/versionpatch.sh ..."
+ echo
+ echo "example:"
+ echo " ./scripts/versionpatch.sh unversioned v8.0.0 v7.0.0"
+ exit
+fi
+
+src_version=$1
+
+shift
+for version in $*; do
+ echo Patching in `pwd`
+
+ # copy new files
+ pushd versions/$src_version > /dev/null
+ for f in $(git ls-files -o --exclude-standard); do
+ mkdir -p ../$version/$(dirname $f)
+ cp $f ../$version/$(dirname $f)/
+ done
+ popd > /dev/null
+
+ # patch changes in existing files
+ pushd versions/$version > /dev/null
+ git diff -- ../$src_version | patch -p4
+ popd > /dev/null
+done
diff --git a/docs/server.js b/docs/server.js
new file mode 100644
index 00000000000000..88606070f8a1c9
--- /dev/null
+++ b/docs/server.js
@@ -0,0 +1,56 @@
+const { createServer } = require('http');
+const { parse } = require('url');
+const next = require('next');
+
+const dev = process.env.NODE_ENV !== 'production';
+const app = next({ dev });
+const handle = app.getRequestHandler();
+
+const LATEST_VERSION = 'v' + require('./package.json').version;
+const { WORKFLOW, DISTRIBUTION, EXPOKIT } = require('./transition/sections');
+
+const aliases = [
+ { path: 'workflow', files: WORKFLOW },
+ { path: 'distribution', files: DISTRIBUTION },
+ { path: 'expokit', files: EXPOKIT },
+];
+
+app.prepare().then(() => {
+ createServer((req, res) => {
+ const parsedUrl = parse(req.url, true);
+ let { pathname, query } = parsedUrl;
+
+ if (pathname.endsWith('.html')) {
+ pathname = pathname.replace('.html', '');
+ }
+
+ if (pathname.endsWith('/') && pathname.length > 1) {
+ pathname = pathname.slice(0, -1);
+ }
+
+ // `latest` URLs should render the latest version
+ const splitPath = pathname.split('/');
+ if (splitPath[2] === 'latest') {
+ splitPath[2] = LATEST_VERSION;
+ req.originalPath = parsedUrl;
+ app.render(req, res, splitPath.join('/'), query);
+ } else {
+ // Alias old URLs to new ones
+ for (let i = 0; i < aliases.length; i++) {
+ let alias = aliases[i];
+ if (splitPath[3] === 'guides' && alias.files.indexOf(splitPath[4]) > -1) {
+ splitPath[3] = alias.path;
+ req.originalPath = parsedUrl;
+ app.render(req, res, splitPath.join('/'), query);
+ return;
+ }
+ }
+
+ req.originalPath = parsedUrl;
+ app.render(req, res, pathname, query);
+ }
+ }).listen(3000, err => {
+ if (err) throw err;
+ console.log('> Ready on http://localhost:3000');
+ });
+});
diff --git a/docs/static/fonts/MaisonNeue-Bold.woff b/docs/static/fonts/MaisonNeue-Bold.woff
new file mode 100644
index 00000000000000..8d5fb70dfeb700
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Bold.woff differ
diff --git a/docs/static/fonts/MaisonNeue-Bold.woff2 b/docs/static/fonts/MaisonNeue-Bold.woff2
new file mode 100644
index 00000000000000..1b478cc6794eea
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Bold.woff2 differ
diff --git a/docs/static/fonts/MaisonNeue-Book.woff b/docs/static/fonts/MaisonNeue-Book.woff
new file mode 100644
index 00000000000000..356a64e9400ca3
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Book.woff differ
diff --git a/docs/static/fonts/MaisonNeue-Book.woff2 b/docs/static/fonts/MaisonNeue-Book.woff2
new file mode 100644
index 00000000000000..5f04b426afd6b1
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Book.woff2 differ
diff --git a/docs/static/fonts/MaisonNeue-BookItalic.woff b/docs/static/fonts/MaisonNeue-BookItalic.woff
new file mode 100644
index 00000000000000..664dbfa264417e
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-BookItalic.woff differ
diff --git a/docs/static/fonts/MaisonNeue-BookItalic.woff2 b/docs/static/fonts/MaisonNeue-BookItalic.woff2
new file mode 100644
index 00000000000000..79f9f58d12b30c
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-BookItalic.woff2 differ
diff --git a/docs/static/fonts/MaisonNeue-Demi.woff b/docs/static/fonts/MaisonNeue-Demi.woff
new file mode 100644
index 00000000000000..89a3f2f51bcacf
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Demi.woff differ
diff --git a/docs/static/fonts/MaisonNeue-Demi.woff2 b/docs/static/fonts/MaisonNeue-Demi.woff2
new file mode 100644
index 00000000000000..a9ceffddd1ab97
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Demi.woff2 differ
diff --git a/docs/static/fonts/MaisonNeue-Light.woff b/docs/static/fonts/MaisonNeue-Light.woff
new file mode 100644
index 00000000000000..5f7393df4046ac
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Light.woff differ
diff --git a/docs/static/fonts/MaisonNeue-Light.woff2 b/docs/static/fonts/MaisonNeue-Light.woff2
new file mode 100644
index 00000000000000..ab65f2acd67fa6
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Light.woff2 differ
diff --git a/docs/static/fonts/MaisonNeue-Mono.woff b/docs/static/fonts/MaisonNeue-Mono.woff
new file mode 100644
index 00000000000000..32eb874f6a4f27
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Mono.woff differ
diff --git a/docs/static/fonts/MaisonNeue-Mono.woff2 b/docs/static/fonts/MaisonNeue-Mono.woff2
new file mode 100644
index 00000000000000..5abefd86088445
Binary files /dev/null and b/docs/static/fonts/MaisonNeue-Mono.woff2 differ
diff --git a/docs/static/images/favicon.ico b/docs/static/images/favicon.ico
new file mode 100644
index 00000000000000..3412b6db66c6b8
Binary files /dev/null and b/docs/static/images/favicon.ico differ
diff --git a/docs/static/libs/algolia/algolia-mobile.css b/docs/static/libs/algolia/algolia-mobile.css
new file mode 100644
index 00000000000000..ddc4879dad352b
--- /dev/null
+++ b/docs/static/libs/algolia/algolia-mobile.css
@@ -0,0 +1,36 @@
+
+/* Add mobile-responsiveness to Algolia search dropdown as suggested by
+ LukyVj who works at Algolia: https://github.com/algolia/docsearch/issues/181 */
+
+@media (max-width: 768px) {
+
+ /* Abi added this */
+ .searchbox {
+ width: 100%;
+ margin-bottom: 0px;
+ }
+
+ .algolia-autocomplete .ds-dropdown-menu {
+ max-width: calc(100vw - 32px) !important;
+ min-width: calc(100vw - 32px) !important;
+ width: calc(100vw - 32px) !important;
+
+ /* Abi removed this */
+ /*margin-left: 16px !important;*/
+ }
+ .algolia-autocomplete .algolia-docsearch-suggestion--content {
+ width: 100% !important;
+ padding-left: 0 !important;
+ }
+ .algolia-autocomplete .algolia-docsearch-suggestion--content:before {
+ display: none !important;
+ }
+ .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column {
+ display: none !important;
+ }
+
+ /* Abi added this; neccessary to prevent mobile Safari from zooming in */
+ .algolia-autocomplete .searchbox__input {
+ font-size: 16px;
+ }
+}
\ No newline at end of file
diff --git a/docs/static/libs/algolia/algolia.min.css b/docs/static/libs/algolia/algolia.min.css
new file mode 100644
index 00000000000000..9fe1aad83e3efd
--- /dev/null
+++ b/docs/static/libs/algolia/algolia.min.css
@@ -0,0 +1,2 @@
+.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;box-sizing:border-box;visibility:visible!important}.searchbox .algolia-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:1;position:relative}.searchbox__input{display:inline-block;box-sizing:border-box;-webkit-transition:box-shadow .4s ease,background .4s ease;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0;padding-right:26px;padding-left:32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox__input::-webkit-input-placeholder{color:#aaa}.searchbox__input::-moz-placeholder{color:#aaa}.searchbox__input:-ms-input-placeholder{color:#aaa}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:a;animation-name:a;-webkit-animation-duration:.15s;animation-duration:.15s}@-webkit-keyframes a{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes a{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:1;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:2;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);border-radius:2px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:2;margin-top:8px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight{color:inherit;background:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;display:none;padding-left:0;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column .algolia-docsearch-suggestion--highlight{background-color:inherit;color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary .algolia-docsearch-suggestion--subcategory-column{display:block}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-column,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.algolia-autocomplete .algolia-docsearch-footer{width:110px;height:20px;z-index:3;margin-top:10.66667px;float:right;font-size:0;line-height:0}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;utf8,");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block}
+/*# sourceMappingURL=docsearch.min.css.map */
\ No newline at end of file
diff --git a/docs/static/libs/nprogress/nprogress.css b/docs/static/libs/nprogress/nprogress.css
new file mode 100644
index 00000000000000..c1977c6049a220
--- /dev/null
+++ b/docs/static/libs/nprogress/nprogress.css
@@ -0,0 +1 @@
+#nprogress{pointer-events:none;}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px;}#nprogress .peg{display:block;position:absolute;right:0px;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1.0;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translate(0px,-4px);}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px;}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite;}.nprogress-custom-parent{overflow:hidden;position:relative;}.nprogress-custom-parent #nprogress .spinner,.nprogress-custom-parent #nprogress .bar{position:absolute;}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);}}@keyframes nprogress-spinner{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}
\ No newline at end of file
diff --git a/docs/static/libs/nprogress/nprogress.js b/docs/static/libs/nprogress/nprogress.js
new file mode 100644
index 00000000000000..884d4689d73dc0
--- /dev/null
+++ b/docs/static/libs/nprogress/nprogress.js
@@ -0,0 +1,23 @@
+;(function(root,factory){if(typeof define==='function'&&define.amd){define(factory);}else if(typeof exports==='object'){module.exports=factory();}else{root.NProgress=factory();}})(this,function(){var NProgress={};NProgress.version='0.2.0';var Settings=NProgress.settings={minimum:0.08,easing:'ease',positionUsing:'',speed:200,trickle:true,trickleRate:0.02,trickleSpeed:800,showSpinner:true,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:'body',template:''};NProgress.configure=function(options){var key,value;for(key in options){value=options[key];if(value!==undefined&&options.hasOwnProperty(key))Settings[key]=value;}
+return this;};NProgress.status=null;NProgress.set=function(n){var started=NProgress.isStarted();n=clamp(n,Settings.minimum,1);NProgress.status=(n===1?null:n);var progress=NProgress.render(!started),bar=progress.querySelector(Settings.barSelector),speed=Settings.speed,ease=Settings.easing;progress.offsetWidth;queue(function(next){if(Settings.positionUsing==='')Settings.positionUsing=NProgress.getPositioningCSS();css(bar,barPositionCSS(n,speed,ease));if(n===1){css(progress,{transition:'none',opacity:1});progress.offsetWidth;setTimeout(function(){css(progress,{transition:'all '+ speed+'ms linear',opacity:0});setTimeout(function(){NProgress.remove();next();},speed);},speed);}else{setTimeout(next,speed);}});return this;};NProgress.isStarted=function(){return typeof NProgress.status==='number';};NProgress.start=function(){if(!NProgress.status)NProgress.set(0);var work=function(){setTimeout(function(){if(!NProgress.status)return;NProgress.trickle();work();},Settings.trickleSpeed);};if(Settings.trickle)work();return this;};NProgress.done=function(force){if(!force&&!NProgress.status)return this;return NProgress.inc(0.3+ 0.5*Math.random()).set(1);};NProgress.inc=function(amount){var n=NProgress.status;if(!n){return NProgress.start();}else{if(typeof amount!=='number'){amount=(1- n)*clamp(Math.random()*n,0.1,0.95);}
+n=clamp(n+ amount,0,0.994);return NProgress.set(n);}};NProgress.trickle=function(){return NProgress.inc(Math.random()*Settings.trickleRate);};(function(){var initial=0,current=0;NProgress.promise=function($promise){if(!$promise||$promise.state()==="resolved"){return this;}
+if(current===0){NProgress.start();}
+initial++;current++;$promise.always(function(){current--;if(current===0){initial=0;NProgress.done();}else{NProgress.set((initial- current)/ initial);
+}});return this;};})();NProgress.render=function(fromStart){if(NProgress.isRendered())return document.getElementById('nprogress');addClass(document.documentElement,'nprogress-busy');var progress=document.createElement('div');progress.id='nprogress';progress.innerHTML=Settings.template;var bar=progress.querySelector(Settings.barSelector),perc=fromStart?'-100':toBarPerc(NProgress.status||0),parent=document.querySelector(Settings.parent),spinner;css(bar,{transition:'all 0 linear',transform:'translate3d('+ perc+'%,0,0)'});if(!Settings.showSpinner){spinner=progress.querySelector(Settings.spinnerSelector);spinner&&removeElement(spinner);}
+if(parent!=document.body){addClass(parent,'nprogress-custom-parent');}
+parent.appendChild(progress);return progress;};NProgress.remove=function(){removeClass(document.documentElement,'nprogress-busy');removeClass(document.querySelector(Settings.parent),'nprogress-custom-parent');var progress=document.getElementById('nprogress');progress&&removeElement(progress);};NProgress.isRendered=function(){return!!document.getElementById('nprogress');};NProgress.getPositioningCSS=function(){var bodyStyle=document.body.style;var vendorPrefix=('WebkitTransform'in bodyStyle)?'Webkit':('MozTransform'in bodyStyle)?'Moz':('msTransform'in bodyStyle)?'ms':('OTransform'in bodyStyle)?'O':'';if(vendorPrefix+'Perspective'in bodyStyle){return'translate3d';}else if(vendorPrefix+'Transform'in bodyStyle){return'translate';}else{return'margin';}};function clamp(n,min,max){if(nmax)return max;return n;}
+function toBarPerc(n){return(-1+ n)*100;}
+function barPositionCSS(n,speed,ease){var barCSS;if(Settings.positionUsing==='translate3d'){barCSS={transform:'translate3d('+toBarPerc(n)+'%,0,0)'};}else if(Settings.positionUsing==='translate'){barCSS={transform:'translate('+toBarPerc(n)+'%,0)'};}else{barCSS={'margin-left':toBarPerc(n)+'%'};}
+barCSS.transition='all '+speed+'ms '+ease;return barCSS;}
+var queue=(function(){var pending=[];function next(){var fn=pending.shift();if(fn){fn(next);}}
+return function(fn){pending.push(fn);if(pending.length==1)next();};})();var css=(function(){var cssPrefixes=['Webkit','O','Moz','ms'],cssProps={};function camelCase(string){return string.replace(/^-ms-/,'ms-').replace(/-([\da-z])/gi,function(match,letter){return letter.toUpperCase();});}
+function getVendorProp(name){var style=document.body.style;if(name in style)return name;var i=cssPrefixes.length,capName=name.charAt(0).toUpperCase()+ name.slice(1),vendorName;while(i--){vendorName=cssPrefixes[i]+ capName;if(vendorName in style)return vendorName;}
+return name;}
+function getStyleProp(name){name=camelCase(name);return cssProps[name]||(cssProps[name]=getVendorProp(name));}
+function applyCss(element,prop,value){prop=getStyleProp(prop);element.style[prop]=value;}
+return function(element,properties){var args=arguments,prop,value;if(args.length==2){for(prop in properties){value=properties[prop];if(value!==undefined&&properties.hasOwnProperty(prop))applyCss(element,prop,value);}}else{applyCss(element,args[1],args[2]);}}})();function hasClass(element,name){var list=typeof element=='string'?element:classList(element);return list.indexOf(' '+ name+' ')>=0;}
+function addClass(element,name){var oldList=classList(element),newList=oldList+ name;if(hasClass(oldList,name))return;element.className=newList.substring(1);}
+function removeClass(element,name){var oldList=classList(element),newList;if(!hasClass(element,name))return;newList=oldList.replace(' '+ name+' ',' ');element.className=newList.substring(1,newList.length- 1);}
+function classList(element){return(' '+(element.className||'')+' ').replace(/\s+/gi,' ');}
+function removeElement(element){element&&element.parentNode&&element.parentNode.removeChild(element);}
+return NProgress;});
\ No newline at end of file
diff --git a/docs/static/libs/prism/prism-coy.css b/docs/static/libs/prism/prism-coy.css
new file mode 100644
index 00000000000000..ab7e4f1f6f622b
--- /dev/null
+++ b/docs/static/libs/prism/prism-coy.css
@@ -0,0 +1,180 @@
+/**
+ * prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML
+ * Based on https://github.com/tshedor/workshop-wp-theme (Example: http://workshop.kansan.com/category/sessions/basics or http://workshop.timshedor.com/category/sessions/basics);
+ * @author Tim Shedor
+ */
+
+/* Margin bottom to accomodate shadow */
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ margin-bottom: 1em;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+ position: relative;
+ padding: .2em;
+ border-radius: 0.3em;
+ color: #c92c2c;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ display: inline;
+ white-space: normal;
+}
+
+pre[class*="language-"]:before,
+pre[class*="language-"]:after {
+ content: '';
+ z-index: -2;
+ display: block;
+ position: absolute;
+ bottom: 0.75em;
+ left: 0.18em;
+ width: 40%;
+ height: 20%;
+ max-height: 13em;
+ /* Note (Abi): Removed because it was causing weird artifacts. What's the purpose of this code? */
+ /*box-shadow: 0px 13px 8px #979797;
+ -webkit-transform: rotate(-2deg);
+ -moz-transform: rotate(-2deg);
+ -ms-transform: rotate(-2deg);
+ -o-transform: rotate(-2deg);
+ transform: rotate(-2deg);*/
+}
+
+:not(pre) > code[class*="language-"]:after,
+pre[class*="language-"]:after {
+ right: 0.75em;
+ left: auto;
+ -webkit-transform: rotate(2deg);
+ -moz-transform: rotate(2deg);
+ -ms-transform: rotate(2deg);
+ -o-transform: rotate(2deg);
+ transform: rotate(2deg);
+}
+
+.token.comment,
+.token.block-comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: #7D8B99;
+}
+
+.token.punctuation {
+ color: #5F6364;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.function-name,
+.token.constant,
+.token.symbol,
+.token.deleted {
+ color: #c92c2c;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.function,
+.token.builtin,
+.token.inserted {
+ color: #2f9c0a;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.token.variable {
+ color: #a67f59;
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword,
+.token.class-name {
+ color: #1990b8;
+}
+
+.token.regex,
+.token.important {
+ color: #e90;
+}
+
+.language-css .token.string,
+.style .token.string {
+ color: #a67f59;
+}
+
+.token.important {
+ font-weight: normal;
+}
+
+.token.bold {
+ font-weight: bold;
+}
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
+
+.namespace {
+ opacity: .7;
+}
+
+@media screen and (max-width: 767px) {
+ pre[class*="language-"]:before,
+ pre[class*="language-"]:after {
+ bottom: 14px;
+ box-shadow: none;
+ }
+
+}
+
+/* Plugin styles */
+.token.tab:not(:empty):before,
+.token.cr:before,
+.token.lf:before {
+ color: #e0d7d1;
+}
+
+/* Plugin styles: Line Numbers */
+pre[class*="language-"].line-numbers {
+ padding-left: 0;
+}
+
+pre[class*="language-"].line-numbers code {
+ padding-left: 3.8em;
+}
+
+pre[class*="language-"].line-numbers .line-numbers-rows {
+ left: 0;
+}
+
+/* Plugin styles: Line Highlight */
+pre[class*="language-"][data-line] {
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-left: 0;
+}
+pre[data-line] code {
+ position: relative;
+ padding-left: 4em;
+}
+pre .line-highlight {
+ margin-top: 0;
+}
+
+.searchbox__input {
+ border-radius: 5px !important;
+ box-shadow: inset 0 0 0 1px #dddddd;
+}
diff --git a/docs/static/libs/prism/prism.css b/docs/static/libs/prism/prism.css
new file mode 100644
index 00000000000000..f308416a2c744c
--- /dev/null
+++ b/docs/static/libs/prism/prism.css
@@ -0,0 +1,138 @@
+/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+
+code[class*="language-"],
+pre[class*="language-"] {
+ color: black;
+ background: none;
+ text-shadow: 0 1px white;
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
+code[class*="language-"]::selection, code[class*="language-"] ::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+}
+
+@media print {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+ padding: 1em;
+ margin: .5em 0;
+ overflow: auto;
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+ background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+ padding: .1em;
+ border-radius: .3em;
+ white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+ color: slategray;
+}
+
+.token.punctuation {
+ color: #999;
+}
+
+.namespace {
+ opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+ color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+ color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+ color: #a67f59;
+ background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+ color: #07a;
+}
+
+.token.function {
+ color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+ color: #e90;
+}
+
+.token.important,
+.token.bold {
+ font-weight: bold;
+}
+.token.italic {
+ font-style: italic;
+}
+
+.token.entity {
+ cursor: help;
+}
diff --git a/docs/static/libs/prism/prism.js b/docs/static/libs/prism/prism.js
new file mode 100644
index 00000000000000..ab0d8fa1cdd19f
--- /dev/null
+++ b/docs/static/libs/prism/prism.js
@@ -0,0 +1,468 @@
+/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript */
+var _self = 'undefined' != typeof window
+ ? window
+ : 'undefined' != typeof WorkerGlobalScope && self instanceof WorkerGlobalScope
+ ? self
+ : {},
+ Prism = (function() {
+ var e = /\blang(?:uage)?-(\w+)\b/i,
+ t = 0,
+ n = (_self.Prism = {
+ manual: _self.Prism && _self.Prism.manual,
+ util: {
+ encode: function(e) {
+ return e instanceof a
+ ? new a(e.type, n.util.encode(e.content), e.alias)
+ : 'Array' === n.util.type(e)
+ ? e.map(n.util.encode)
+ : e
+ .replace(/&/g, '&')
+ .replace(/ e.length) return
+ if (!(w instanceof s)) {
+ h.lastIndex = 0
+ var _ = h.exec(w), P = 1
+ if (!_ && m && b != t.length - 1) {
+ if (((h.lastIndex = k), (_ = h.exec(e)), !_)) break
+ for (
+ var A = _.index + (d ? _[1].length : 0),
+ j = _.index + _[0].length,
+ x = b,
+ O = k,
+ S = t.length;
+ S > x && (j > O || (!t[x].type && !t[x - 1].greedy));
+ ++x
+ )
+ (O += t[x].length), A >= O && (++b, (k = O))
+ if (t[b] instanceof s || t[x - 1].greedy) continue
+ ;(P = x - b), (w = e.slice(k, O)), (_.index -= k)
+ }
+ if (_) {
+ d && (p = _[1].length)
+ var A = _.index + p,
+ _ = _[0].slice(p),
+ j = A + _.length,
+ N = w.slice(0, A),
+ C = w.slice(j),
+ E = [b, P]
+ N && (++b, (k += N.length), E.push(N))
+ var I = new s(u, f ? n.tokenize(_, f) : _, y, _, m)
+ if (
+ (E.push(I), C &&
+ E.push(C), Array.prototype.splice.apply(t, E), 1 !=
+ P && n.matchGrammar(e, t, a, b, k, !0, u), l)
+ )
+ break
+ } else if (l) break
+ }
+ }
+ }
+ }
+ },
+ tokenize: function(e, t) {
+ var a = [e], r = t.rest
+ if (r) {
+ for (var i in r)
+ t[i] = r[i]
+ delete t.rest
+ }
+ return n.matchGrammar(e, a, t, 0, 0, !1), a
+ },
+ hooks: {
+ all: {},
+ add: function(e, t) {
+ var a = n.hooks.all
+ ;(a[e] = a[e] || []), a[e].push(t)
+ },
+ run: function(e, t) {
+ var a = n.hooks.all[e]
+ if (a && a.length) for (var r, i = 0; (r = a[i++]); ) r(t)
+ }
+ }
+ }),
+ a = (n.Token = function(e, t, n, a, r) {
+ ;(this.type = e), (this.content = t), (this.alias = n), (this.length =
+ 0 | (a || '').length), (this.greedy = !!r)
+ })
+ if (
+ ((a.stringify = function(e, t, r) {
+ if ('string' == typeof e) return e
+ if ('Array' === n.util.type(e))
+ return e
+ .map(function(n) {
+ return a.stringify(n, t, e)
+ })
+ .join('')
+ var i = {
+ type: e.type,
+ content: a.stringify(e.content, t, r),
+ tag: 'span',
+ classes: ['token', e.type],
+ attributes: {},
+ language: t,
+ parent: r
+ }
+ if (
+ ('comment' == i.type && (i.attributes.spellcheck = 'true'), e.alias)
+ ) {
+ var l = 'Array' === n.util.type(e.alias) ? e.alias : [e.alias]
+ Array.prototype.push.apply(i.classes, l)
+ }
+ n.hooks.run('wrap', i)
+ var o = Object.keys(i.attributes)
+ .map(function(e) {
+ return (
+ e + '="' + (i.attributes[e] || '').replace(/"/g, '"') + '"'
+ )
+ })
+ .join(' ')
+ return (
+ '<' +
+ i.tag +
+ ' class="' +
+ i.classes.join(' ') +
+ '"' +
+ (o ? ' ' + o : '') +
+ '>' +
+ i.content +
+ '' +
+ i.tag +
+ '>'
+ )
+ }), !_self.document)
+ )
+ return _self.addEventListener
+ ? (_self.addEventListener(
+ 'message',
+ function(e) {
+ var t = JSON.parse(e.data),
+ a = t.language,
+ r = t.code,
+ i = t.immediateClose
+ _self.postMessage(n.highlight(r, n.languages[a], a)), i &&
+ _self.close()
+ },
+ !1
+ ), _self.Prism)
+ : _self.Prism
+ var r =
+ document.currentScript ||
+ [].slice.call(document.getElementsByTagName('script')).pop()
+ return r &&
+ ((n.filename = r.src), n.manual ||
+ r.hasAttribute('data-manual') ||
+ ('loading' !== document.readyState
+ ? window.requestAnimationFrame
+ ? window.requestAnimationFrame(n.highlightAll)
+ : window.setTimeout(n.highlightAll, 16)
+ : document.addEventListener(
+ 'DOMContentLoaded',
+ n.highlightAll
+ ))), _self.Prism
+ })()
+'undefined' != typeof module &&
+ module.exports &&
+ (module.exports = Prism), 'undefined' != typeof global &&
+ (global.Prism = Prism)
+;(Prism.languages.markup = {
+ comment: //,
+ prolog: /<\?[\s\S]+?\?>/,
+ doctype: //i,
+ cdata: //i,
+ tag: {
+ pattern: /<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\s\S])*\1|[^\s'">=]+))?)*\s*\/?>/i,
+ inside: {
+ tag: {
+ pattern: /^<\/?[^\s>\/]+/i,
+ inside: { punctuation: /^<\/?/, namespace: /^[^\s>\/:]+:/ }
+ },
+ 'attr-value': {
+ pattern: /=(?:('|")[\s\S]*?(\1)|[^\s>]+)/i,
+ inside: { punctuation: /[=>"']/ }
+ },
+ punctuation: /\/?>/,
+ 'attr-name': {
+ pattern: /[^\s>\/]+/,
+ inside: { namespace: /^[^\s>\/:]+:/ }
+ }
+ }
+ },
+ entity: /?[\da-z]{1,8};/i
+}), (Prism.languages.markup.tag.inside['attr-value'].inside.entity =
+ Prism.languages.markup.entity), Prism.hooks.add('wrap', function(a) {
+ 'entity' === a.type && (a.attributes.title = a.content.replace(/&/, '&'))
+}), (Prism.languages.xml = Prism.languages.markup), (Prism.languages.html =
+ Prism.languages.markup), (Prism.languages.mathml =
+ Prism.languages.markup), (Prism.languages.svg = Prism.languages.markup)
+;(Prism.languages.css = {
+ comment: /\/\*[\s\S]*?\*\//,
+ atrule: { pattern: /@[\w-]+?.*?(;|(?=\s*\{))/i, inside: { rule: /@[\w-]+/ } },
+ url: /url\((?:(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,
+ selector: /[^\{\}\s][^\{\};]*?(?=\s*\{)/,
+ string: {
+ pattern: /("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
+ greedy: !0
+ },
+ property: /(\b|\B)[\w-]+(?=\s*:)/i,
+ important: /\B!important\b/i,
+ function: /[-a-z0-9]+(?=\()/i,
+ punctuation: /[(){};:]/
+}), (Prism.languages.css.atrule.inside.rest = Prism.util.clone(
+ Prism.languages.css
+)), Prism.languages.markup &&
+ (Prism.languages.insertBefore('markup', 'tag', {
+ style: {
+ pattern: /(