diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_images/add-suggestion.png b/_images/add-suggestion.png new file mode 100644 index 0000000..0a28544 Binary files /dev/null and b/_images/add-suggestion.png differ diff --git a/_images/all-checks-failed.png b/_images/all-checks-failed.png new file mode 100644 index 0000000..5175684 Binary files /dev/null and b/_images/all-checks-failed.png differ diff --git a/_images/animation.mp4 b/_images/animation.mp4 new file mode 100644 index 0000000..f037fd4 Binary files /dev/null and b/_images/animation.mp4 differ diff --git a/_images/bare-repository.png b/_images/bare-repository.png new file mode 100644 index 0000000..45286f9 Binary files /dev/null and b/_images/bare-repository.png differ diff --git a/_images/branches.png b/_images/branches.png new file mode 100644 index 0000000..2464b1c Binary files /dev/null and b/_images/branches.png differ diff --git a/_images/check-details.png b/_images/check-details.png new file mode 100644 index 0000000..456a247 Binary files /dev/null and b/_images/check-details.png differ diff --git a/_images/clone-address.png b/_images/clone-address.png new file mode 100644 index 0000000..abdbd85 Binary files /dev/null and b/_images/clone-address.png differ diff --git a/_images/clone-of-fork.png b/_images/clone-of-fork.png new file mode 100644 index 0000000..de1f78d Binary files /dev/null and b/_images/clone-of-fork.png differ diff --git a/_images/clone.png b/_images/clone.png new file mode 100644 index 0000000..ad948ba Binary files /dev/null and b/_images/clone.png differ diff --git a/_images/comment.png b/_images/comment.png new file mode 100644 index 0000000..6ae948c Binary files /dev/null and b/_images/comment.png differ diff --git a/_images/commit-suggestion.png b/_images/commit-suggestion.png new file mode 100644 index 0000000..4ec4bb6 Binary files /dev/null and b/_images/commit-suggestion.png differ diff --git a/_images/create-repository-with-readme.png b/_images/create-repository-with-readme.png new file mode 100644 index 0000000..2411834 Binary files /dev/null and b/_images/create-repository-with-readme.png differ diff --git a/_images/create-repository.png b/_images/create-repository.png new file mode 100644 index 0000000..80fb99c Binary files /dev/null and b/_images/create-repository.png differ diff --git a/_images/draft-pr-wip.png b/_images/draft-pr-wip.png new file mode 100644 index 0000000..12181d5 Binary files /dev/null and b/_images/draft-pr-wip.png differ diff --git a/_images/draft-pr.png b/_images/draft-pr.png new file mode 100644 index 0000000..2099349 Binary files /dev/null and b/_images/draft-pr.png differ diff --git a/_images/files-changed.png b/_images/files-changed.png new file mode 100644 index 0000000..7b58a22 Binary files /dev/null and b/_images/files-changed.png differ diff --git a/_images/fork-after-update.png b/_images/fork-after-update.png new file mode 100644 index 0000000..38fc8c6 Binary files /dev/null and b/_images/fork-after-update.png differ diff --git a/_images/fork.png b/_images/fork.png new file mode 100644 index 0000000..e658473 Binary files /dev/null and b/_images/fork.png differ diff --git a/_images/forkandclone.png b/_images/forkandclone.png new file mode 100644 index 0000000..d050183 Binary files /dev/null and b/_images/forkandclone.png differ diff --git a/_images/gophers.png b/_images/gophers.png new file mode 100644 index 0000000..741406b Binary files /dev/null and b/_images/gophers.png differ diff --git a/_images/history.png b/_images/history.png new file mode 100644 index 0000000..a3a0d99 Binary files /dev/null and b/_images/history.png differ diff --git a/_images/issue-number.png b/_images/issue-number.png new file mode 100644 index 0000000..be273a2 Binary files /dev/null and b/_images/issue-number.png differ diff --git a/_images/leave-comment.png b/_images/leave-comment.png new file mode 100644 index 0000000..95e33c2 Binary files /dev/null and b/_images/leave-comment.png differ diff --git a/_images/license.png b/_images/license.png new file mode 100644 index 0000000..ec5cf85 Binary files /dev/null and b/_images/license.png differ diff --git a/_images/network.png b/_images/network.png new file mode 100644 index 0000000..26a6d63 Binary files /dev/null and b/_images/network.png differ diff --git a/_images/new-repository.png b/_images/new-repository.png new file mode 100644 index 0000000..aff7677 Binary files /dev/null and b/_images/new-repository.png differ diff --git a/_images/owl.png b/_images/owl.png new file mode 100644 index 0000000..7f90e9f Binary files /dev/null and b/_images/owl.png differ diff --git a/_images/pull-request-form.png b/_images/pull-request-form.png new file mode 100644 index 0000000..c6c6af8 Binary files /dev/null and b/_images/pull-request-form.png differ diff --git a/_images/sharing.png b/_images/sharing.png new file mode 100644 index 0000000..8368d6b Binary files /dev/null and b/_images/sharing.png differ diff --git a/_images/stars.png b/_images/stars.png new file mode 100644 index 0000000..f4c5cf7 Binary files /dev/null and b/_images/stars.png differ diff --git a/_images/sync-fork.png b/_images/sync-fork.png new file mode 100644 index 0000000..848a054 Binary files /dev/null and b/_images/sync-fork.png differ diff --git a/_images/unwatch.png b/_images/unwatch.png new file mode 100644 index 0000000..020a6c9 Binary files /dev/null and b/_images/unwatch.png differ diff --git a/_images/upload-files.png b/_images/upload-files.png new file mode 100644 index 0000000..fd3ef8c Binary files /dev/null and b/_images/upload-files.png differ diff --git a/_images/vscode-authorize.png b/_images/vscode-authorize.png new file mode 100644 index 0000000..4d45dc2 Binary files /dev/null and b/_images/vscode-authorize.png differ diff --git a/_images/vscode-publish-branch.png b/_images/vscode-publish-branch.png new file mode 100644 index 0000000..323ffb2 Binary files /dev/null and b/_images/vscode-publish-branch.png differ diff --git a/_images/vscode-publish-to-github1.png b/_images/vscode-publish-to-github1.png new file mode 100644 index 0000000..883e756 Binary files /dev/null and b/_images/vscode-publish-to-github1.png differ diff --git a/_images/vscode-publish-to-github2.png b/_images/vscode-publish-to-github2.png new file mode 100644 index 0000000..d0055db Binary files /dev/null and b/_images/vscode-publish-to-github2.png differ diff --git a/_images/vscode-publish-to-github3.png b/_images/vscode-publish-to-github3.png new file mode 100644 index 0000000..0f68d0e Binary files /dev/null and b/_images/vscode-publish-to-github3.png differ diff --git a/_images/vscode-start.png b/_images/vscode-start.png new file mode 100644 index 0000000..e783080 Binary files /dev/null and b/_images/vscode-start.png differ diff --git a/_sources/collaboration.md.txt b/_sources/collaboration.md.txt new file mode 100644 index 0000000..12efbc1 --- /dev/null +++ b/_sources/collaboration.md.txt @@ -0,0 +1,13 @@ +(collaboration)= + +# Collaborative version control and code review + +```{toctree} +:maxdepth: 1 + +collaboration/concepts.md +collaboration/same-repository.md +collaboration/code-review.md +collaboration/forking-workflow.md +collaboration/demo-discussion.md +``` diff --git a/_sources/collaboration/code-review.md.txt b/_sources/collaboration/code-review.md.txt new file mode 100644 index 0000000..4a55b03 --- /dev/null +++ b/_sources/collaboration/code-review.md.txt @@ -0,0 +1,144 @@ +(code-review)= + +# Practicing code review + +In this episode we will practice the code review process. We will learn how to +ask for changes in a pull request, how to suggest a change in a pull request, +and how to modify a pull request. + +This will enable research groups to work more collaboratively and to not only +improve the code quality but also to learn from each other. + + +## Exercise + +:::{prereq} Exercise preparation +We can continue in the same exercise repository which we have used in the +previous episode. +::: + +:::{exercise} Exercise: Practicing code review (25 min) + +**Technical requirements**: +- If you create the commits locally: [Being able to authenticate to GitHub](https://coderefinery.github.io/installation/ssh/) + +**What is familiar** from previous lessons: +- Creating a branch. +- Committing a change on the new branch. +- Opening and merging pull requests. + +**What will be new** in this exercise: +- As a reviewer, we will learn how to ask for changes in a pull request. +- As a reviewer, we will learn how to suggest a change in a pull request. +- As a submitter, we will learn how to modify a pull request without closing + the incomplete one and opening a new one. + +**Exercise tasks**: +1. Create a new branch and one or few commits: in these improve something but also + deliberately introduce a typo and also a larger mistake which we will want to fix during the code review. +1. Open a pull request towards the main branch. +1. As a reviewer to somebody else's pull request, ask for an improvement and + also directly suggest a change for the small typo. (Hint: + suggestions are possible through the GitHub web interface, view of + a pull request, "Files changed" view, after selecting some lines. + Look for the "±" button.) +1. As the submitter, learn how to accept the suggested change. (Hint: + GitHub web interface, "Files Changed" view.) +1. As the submitter, improve the pull request without having to close and open + a new one: by adding a new commit to the same branch. (Hint: push + to the branch again.) +1. Once the changes are addressed, merge the pull request. +::: + + +## Help and discussion + +From here on out, we don't give detailed steps to the solution. You +need to combine what you know, and the extra info below, in order to +solve the above. + +### How to ask for changes in a pull request + +Technically, there are at least two common ways to ask for changes in a pull +request. + +Either in the comment field of the pull request: +:::{figure} img/code-review/comment.png +:width: 60% +:class: with-border +:alt: Screenshot of a pull request comment field +:::: + +Or by using the "Review changes": +:::{figure} img/code-review/files-changed.png +:width: 100% +:class: with-border +:alt: Screenshot of a pull request navigating to the "Review changes" tab +:::: + +And always please be kind and constructive in your comments. Remember that the +goal is not gate-keeping but **collaborative learning**. + + +### How to suggest a change in a pull request as a reviewer + +If you see a very small problem that is easy to fix, you can suggest a change +as a reviewer. + +Instead of asking the submitter to tiny problem, you can suggest a change by +clicking on the plus sign next to the line number in the "Files changed" tab: +:::{figure} img/code-review/leave-comment.png +:width: 100% +:class: with-border +:alt: Screenshot of leaving a comment to a line in a pull request +:::: + +Here you can comment on specific lines or even line ranges. + +But now the interesting part is to click on the "Add a suggestion" symbol (the +one that looks like plus and minus). Now you can fix the tiny problem (in this +case a typo) and then click on the "Add single comment" button: +:::{figure} img/code-review/add-suggestion.png +:width: 60% +:class: with-border +:alt: Sequence of clicks to add a suggestion to a line in a pull request +:::: + +The result is this and the submitter can accept the change with a single click: +:::{figure} img/code-review/commit-suggestion.png +:width: 60% +:class: with-border +:alt: Screenshot of a pull request with a suggested change +:::: + +After accepting with "Commit suggestion", the improvement gets added to the +pull request. + + +### How to modify a pull request to address the review comments + +If the reviewer asks for changes, it is not necessary to close the pull request +and later open a new one. It can even be counter-productive to do so: This can +fragment the discussion and the history of the pull request and can make it +harder to understand the context of the changes. + +A much better mechanism to recognize that pull requests are not implemented +from a specific commit to a specific branch, but **always from a branch to a +branch**. + +This means that you can make amendments to the pull request by adding new +commits to the same source branch. This way the pull request will be updated +automatically and the reviewer can see the new changes and comment on them. + +The fact that pull requests are from branch to branch also strongly suggests +that it is a good practice to create a new branch for each pull request. +Otherwise you could accidentally modify an open pull request by adding new +commits to the source branch. + + +## Summary + +- Our process isn't just about code now. It's about discussion and + working together to make the whole process better. +- GitHub discussions and reviewing are quite powerful and can make + small changes easy. diff --git a/_sources/collaboration/concepts.md.txt b/_sources/collaboration/concepts.md.txt new file mode 100644 index 0000000..c0a7d27 --- /dev/null +++ b/_sources/collaboration/concepts.md.txt @@ -0,0 +1,69 @@ +(concepts)= + +# Concepts around collaboration + +```{objectives} +- Be able to decide whether to divide work at the branch level or at the repository level. +``` + + +## Commits, branches, repositories, forks, clones + +- **repository**: The project, contains all data and history (commits, branches, tags). +- **commit**: Snapshot of the project, gets a unique identifier (e.g. `c7f0e8bfc718be04525847fc7ac237f470add76e`). +- **branch**: Independent development line. The main development line is often called `main`. +- **tag**: A pointer to one commit, to be able to refer to it later. Like a [commemorative plaque](https://en.wikipedia.org/wiki/Commemorative_plaque) + that you attach to a particular commit (e.g. `phd-printed` or `paper-submitted`). +- **cloning**: Copying the whole repository to your laptop - the first time. It is not necessary to download each file one by one. +- **forking**: Taking a copy of a repository (which is typically not yours) - your + copy (fork) stays on GitHub/GitLab and you can make changes to your copy. + + +## Cloning a repository + +In order to make a complete copy a whole repository, the `git clone` command +can be used. When cloning, all the files, of all or selected branches, of a +repository are copied in one operation. Cloning of a repository is of relevance +in a few different situations: +* Working on your own, cloning is the operation that you can use to create + multiple instances of a repository on, for instance, a personal computer, a + server, and a supercomputer. +* The parent repository could be a repository that you or your colleague own. A + common use case for cloning is when working together within a smaller team + where everyone has read and write access to the same git repository. +* Alternatively, cloning can be made from a public repository of a code that + you would like to use. Perhaps you have no intention to work on the code, but + would like to stay in tune with the latest developments, also in-between + releases of new versions of the code. + + +```{figure} img/overview/forkandclone.png +:alt: Forking and cloning +:width: 100% +:class: with-border + +Forking and cloning +``` + + +## Forking a repository + +When a fork is made on GitHub/GitLab a complete copy, of all or selected +branches, of the repository is made. The copy will reside under a different +account on GitHub/GitLab. Forking of a repository is of high relevance when +working with a git repository to which you do not have write access. +* In the fork repository commits can be made to the base branch (`main` or + `master`), and to other branches. +* The commits that are made within the branches of the fork repository can be + contributed back to the parent repository by means of pull or merge requests. + + +## Synchronizing changes between repositories + +- We need a mechanism to communicate changes between the repositories. +- We will **pull** or **fetch** updates **from** remote repositories (we will soon discuss the difference between pull and fetch). +- We will **push** updates **to** remote repositories. +- We will learn how to suggest changes within repositories on GitHub and across repositories (**pull request**). +- Repositories that are forked or cloned do not automatically synchronize themselves: + We will learn how to update forks (by pulling from the "central" repository). +- A main difference between cloning a repository and forking a repository is that the former is a general operation for generating copies of a repository to different computers, whereas forking is a particular operation implemented on GitHub/GitLab. diff --git a/_sources/collaboration/demo-discussion.md.txt b/_sources/collaboration/demo-discussion.md.txt new file mode 100644 index 0000000..d6b4f28 --- /dev/null +++ b/_sources/collaboration/demo-discussion.md.txt @@ -0,0 +1,5 @@ +(collaboration-demo-discussion)= + +# Conclict resolution, rebasing, and organizational strategies + +(40 min demo and discussion) diff --git a/_sources/collaboration/forking-workflow.md.txt b/_sources/collaboration/forking-workflow.md.txt new file mode 100644 index 0000000..f8c254d --- /dev/null +++ b/_sources/collaboration/forking-workflow.md.txt @@ -0,0 +1,223 @@ +(forking-workflow)= + +# How to contribute changes to repositories that belong to others + +In this episode we prepare you to suggest and contribute changes to +repositories that belong to others. These might be open source projects that +you use in your work. + +We will see how Git and services like GitHub or GitLab can be used to suggest +modification without having to ask for write access to the repository and +accept modifications without having to grant write access to others. + + +## Exercise + +:::{prereq} Exercise preparation +- The exercise repository is now different: + (note the `-forking-exercise`). +- First **fork** the exercise repository to your GitHub account. +- Then **clone your fork** to your computer (if you wish to work locally). +- Double-check that you have forked the correct repository. +::: + +:::{exercise} Exercise: Collaborating within the same repository (25 min) + +**Technical requirements**: +- If you create the commits locally: [Being able to authenticate to GitHub](https://coderefinery.github.io/installation/ssh/) + +**What is familiar** from previous lessons: +- Forking a repository. +- Creating a branch. +- Committing a change on the new branch. +- Opening and merging pull requests. + +**What will be new** in this exercise: +- Opening a pull request towards the upstream repository. +- Pull requests can be coupled with automated testing. +- Learning that your fork can get out of date. +- After the pull requests are merged, updating your fork with the changes. +- Learn how to approach other people's repositories with ideas, changes, and requests. + +**Exercise tasks**: +1. Open an issue in the upstream exercise repository where you describe the + change you want to make. Take note of the issue number. +1. Create a new branch in your fork of the repository. +1. Make a change to the recipe book on the new branch and in the commit cross-reference the issue you opened. + See the walk-through below for how to do this. +1. Open a pull request towards the upstream repository. +1. The instructor will review and merge the pull requests. + During the review, pay attention to the automated test step (here for + demonstration purposes, we test whether the recipe contains an ingredients + and an instructions sections). +1. After few pull requests are merged, update your fork with the changes. +1. Check that in your fork you can see changes from other people's pull requests. +::: + + +## Help and discussion + + +### Help! I don't have permissions to push my local changes + +Maybe you see an error like this one: +```text +Please make sure you have the correct access rights +and the repository exists. +``` + +Or like this one: +```text +failed to push some refs to workshop-material/recipe-book-forking-exercise.git +``` + +In this case you probably try to push the changes not to your fork but to the original repository +and in this exercise you do not have write access to the original repository. + +The simpler solution is to clone again but this time your fork. + +:::{solution} Recovery + +But if you want to keep your local changes, you can change the remote URL to point to your fork. +Check where your remote points to with `git remote --verbose`. + +It should look like this (replace `USER` with your GitHub username): +```console +$ git remote --verbose + +origin git@github.com:USER/recipe-book-forking-exercise.git (fetch) +origin git@github.com:USER/recipe-book-forking-exercise.git (push) +``` + +It should **not** look like this: +```console +$ git remote --verbose + +origin git@github.com:workshop-material/recipe-book-forking-exercise.git (fetch) +origin git@github.com:workshop-material/recipe-book-forking-exercise.git (push) +``` + +In this case you can adjust "origin" to point to your fork with: +```console +$ git remote set-url origin git@github.com:USER/recipe-book-forking-exercise.git +``` +::: + + +### Opening a pull request towards the upstream repository + +We have learned in the previous episode that pull requests are always from +branch to branch. But the branch can be in a different repository. + +When you open a pull request in a fork, by default GitHub will suggest to +direct it towards the default branch of the upstream repository. + +This can be changed and it should always be verified, but in this case this is +exactly what we want to do, from fork towards upstream: +:::{figure} img/forking-workflow/pull-request-form.png +:width: 100% +:class: with-border +:alt: Screenshot of a pull request from fork towards upstream +:::: + + +### Pull requests can be coupled with automated testing + +We added an automated test here just for fun and so that you see that this is +possible to do. + +In this exercise, the test is silly. It will check whether the recipe contains +both an ingredients and an instructions section. + +In this example the test failed: +:::{figure} img/forking-workflow/all-checks-failed.png +:width: 60% +:class: with-border +:alt: Screenshot of a failed test in a pull request +:::: + +Click on the "Details" link to see the details of the failed test: +:::{figure} img/forking-workflow/check-details.png +:width: 60% +:class: with-border +:alt: Screenshot of details why the test failed +:::: + +**How can this be useful?** +- The project can define what kind of tests are expected to pass before a pull + request can be merged. +- The reviewer can see the results of the tests, without having to run them + locally. + +**How does it work?** +- We added a GitHub Actions workflow + to automatically run on each push or pull request towards the `main` branch. + +What tests or steps can you image for your project to run automatically with +each pull request? + + +### How to update your fork with changes from upstream + +This used to be difficult but now it is two mouse clicks. + +Navigate to your fork and notice how GitHub tells you that your fork is behind. +In my case, it is 9 commits behind upstream. To fix this, click on "Sync fork" +and then "Update branch": +:::{figure} img/forking-workflow/sync-fork.png +:width: 80% +:class: with-border +:alt: Screenshot on GitHub fork page showing that the fork is behind +:::: + +After the update my "branch is up to date" with the upstream repository: +:::{figure} img/forking-workflow/fork-after-update.png +:width: 80% +:class: with-border +:alt: Screenshot on GitHub after fork has been updated +:::: + + +### How to approach other people’s repositories with ideas, changes, and requests + +**Contributing very minor changes** +- Clone or fork+clone repository +- Create a branch +- Commit and push change +- Open a pull request or merge request + +**If you observe an issue and have an idea how to fix it** +- Open an issue in the repository you wish to contribute to +- Describe the problem +- If you have a suggestion on how to fix it, describe your suggestion +- Possibly **discuss and get feedback** +- If you are working on the fix, indicate it in the issue so that others know that somebody is working on it and who is working on it +- Submit your fix as pull request or merge request which references/closes the issue + +:::{admonition} Motivation +- **Inform others about an observed problem** +- Make it clear whether this issue is up for grabs or already being worked on +::: + +**If you have an idea for a new feature** +- Open an issue in the repository you wish to contribute to +- In the issue, write a short proposal for your suggested change or new feature +- Motivate why and how you wish to do this +- Also indicate where you are unsure and where you would like feedback +- **Discuss and get feedback before you code** +- Once you start coding, indicate that you are working on it +- Once you are done, submit your new feature as pull request or merge request which references/closes the issue/proposal + +:::{admonition} Motivation +- **Get agreement and feedback before writing 5000 lines of code** which might be rejected +- If we later wonder why something was done, we have the issue/proposal as + reference and can read up on the reasoning behind a code change +::: + + +## Summary + +- This forking workflow lets you propose changes to repositories for + which **you have no write access**. +- This is the way that much modern open-source software works. +- You can now contribute to any project you can view. diff --git a/_sources/collaboration/same-repository.md.txt b/_sources/collaboration/same-repository.md.txt new file mode 100644 index 0000000..1a69d1d --- /dev/null +++ b/_sources/collaboration/same-repository.md.txt @@ -0,0 +1,280 @@ +(same-repository)= + +# Collaborating within the same repository + +In this episode, we will learn how to collaborate within the same repository. +We will learn how to cross-reference issues and pull requests, how to review +pull requests, and how to use draft pull requests. + +This exercise will form a good basis for collaboration that is suitable for +most research groups. + +:::{note} +When you read or hear **pull request**, please think of a **change proposal**. +::: + + +## Exercise + +In this exercise, we will contribute to a repository via a **pull request**. +This means that you propose some change, and then it is accepted (or not). + +::::::{prereq} Exercise preparation + +- **First we need to get access** to the [exercise repository](https://github.com/workshop-material/recipe-book) to which we will + contribute. + - Instructor collects GitHub usernames from learners and adds them as collaborators to the exercise repository + (Settings -> Collaborators and teams -> Manage access -> Add people). + +- **Don't forget to accept the invitation** + - Check + - Alternatively check the inbox for the email account you registered with + GitHub. GitHub emails you an invitation link, but if you don't receive it + you can go to your GitHub notifications in the top right corner. The + maintainer can also "copy invite link" and share it within the group. + +- **Watching and unwatching repositories** + - Now that you are a collaborator, you get notified about new issues and pull + requests via email. + - If you do not wish this, you can "unwatch" a repository (top of + the project page). + - However, we recommend watching repositories you are interested + in. You can learn things from experts just by watching the + activity that come through a popular project. + :::{figure} img/unwatch.png + :alt: Unwatching a repository + + Unwatch a repository by clicking "Unwatch" in the repository view, + then "Participating and @mentions" - this way, you will get + notifications about your own interactions. + ::: +:::::: + +:::{exercise} Exercise: Collaborating within the same repository (25 min) + +**Technical requirements** (from installation instructions): +- If you create the commits locally: [Being able to authenticate to GitHub](https://coderefinery.github.io/installation/ssh/) + +**What is familiar** from the previous workshop day (not repeated here): +- Cloning a repository. +- Creating a branch. +- Committing a change on the new branch. +- Submit a pull request towards the main branch. + +**What will be new** in this exercise: +- If you create the changes locally, you will need to **push** them to the remote repository. +- Learning what a protected branch is and how to modify a protected branch: using a pull request. +- Cross-referencing issues and pull requests. +- Practice to review a pull request. +- Learn about the value of draft pull requests. + +**Exercise tasks**: +1. Start in the [exercise + repository](https://github.com/workshop-material/recipe-book) and open an + issue where you describe the change you want to make. Note down the issue + number since you will need it later. +1. Create a new branch. +1. Make a change to the recipe book on the new branch and in the commit + cross-reference the issue you opened (see the walk-through below + for how to do that). +1. Push your new branch (with the new commit) to the repository you + are working on. +1. Open a pull request towards the main branch. +1. Review somebody else's pull request and give constructive feedback. Merge their pull request. +1. Try to create a new branch with some half-finished work and open a draft + pull request. Verify that the draft pull request cannot be merged since it + is not meant to be merged yet. +::: + + +## Solution and hints + +### (1) Opening an issue + +This is done through the GitHub web interface. For example, you could +give the name of the recipe you want to add (so that others don't add +the same one). It is the "Issues" tab. + + +### (2) Create a new branch. + +If on GitHub, you can make the branch in the web interface. + + +### (3) Make a change adding the recipe + +Add a new file with the recipe in it. Commit the file. In the commit +message, include the note about the issue number, saying that this +will close that issue. + + +#### Cross-referencing issues and pull requests + +Each issue and each pull request gets a number and you can cross-reference them. + +When you open an issue, note down the issue number (in this case it is `#2`): +:::{figure} img/same-repository/issue-number.png +:width: 60% +:class: with-border +:alt: Each issue gets a number +::: + +You can reference this issue number in a commit message or in a pull request, like in this +commit message: +```text +this is the new recipe; fixes #2 +``` + +If you forget to do that in your commit message, you can also reference the issue +in the pull request description. And instead of `fixes` you can also use `closes` or `resolves` +or `fix` or `close` or `resolve` (case insensitive). + +Here are all the keywords that GitHub recognizes: + + +Then observe what happens in the issue once your commit gets merged: it will +automatically close the issue and create a link between the issue and the +commit. This is very useful for tracking what changes were made in response to +which issue and to know from when until when precisely the issue was open. + + +### (4) Push to GitHub as a new branch + +Push the branch to the repository. You should end up with a branch +visible in the GitHub web view. + +This is only necessary if you created the changes locally. If you created the +changes directly on GitHub, you can skip this step. + +:::::{tabs} +::::{group-tab} VS Code +In VS Code, you can "publish the branch" to the remote repository by clicking +the cloud icon in the bottom left corner of the window: +:::{figure} img/same-repository/vscode-publish-branch.png +:width: 60% +:class: with-border +:alt: Publishing a branch in VS Code +::: +:::: + +::::{group-tab} Command line +If you have a local branch `my-branch` and you want to push it to the remote +and make it visible there, first verify what your remote is: +```console +$ git remote --verbose + +origin git@github.com:USER/recipe-book.git (fetch) +origin git@github.com:USER/recipe-book.git (push) +``` + +In this case the remote is called `origin` and refers to the address +git@github.com:USER/recipe-book.git. Both can be used +interchangeably. Make sure it points to the right repository, ideally a +repository that you can write to. + +Now that you have a remote, you can push your branch to it: +```console +$ git push origin my-branch +``` +This will create a new branch on the remote repository with the same name as +your local branch. + +You can also do this: +```console +$ git push --set-upstream origin my-branch +``` +This will connect the local branch and the remote branch so that in the future +you can just type `git push` and `git pull` without specifying the branch name +(but this depends on your Git configuration). + + +**Troubleshooting** + +If you don't have a remote yet, you can add it with (adjust `ADDRESS` to your repository address): +```console +$ git remote add origin ADDRESS +``` + +ADDRESS is the part that you copy from here: +:::{figure} img/same-repository/clone-address.png +:width: 60% +:class: with-border +:alt: Copying the clone address from GitHub +::: + +If the remote points to the wrong place, you can change it with: +```console +$ git remote set-url origin NEWADDRESS +``` +:::: +::::: + + +### (5) Open a pull request towards the main branch + +This is done through the GitHub web interface. + + +### (6) Reviewing pull requests + +You review through the GitHub web interface. + +Checklist for reviewing a pull request: +- Be kind, on the other side is a human who has put effort into this. +- Be constructive: if you see a problem, suggest a solution. +- Towards which branch is this directed? +- Is the title descriptive? +- Is the description informative? +- Scroll down to see commits. +- Scroll down to see the changes. +- If you get incredibly many changes, also consider the license or copyright + and ask where all that code is coming from. +- Again, be kind and constructive. +- Later we will learn how to suggest changes directly in the pull request. + +If someone is new, it's often nice to say something encouraging in the +comments before merging (even if it's just "thanks"). If all is good +and there's not much else to say, you could merge directly. + + +### (7) Draft pull requests + +Try to create a draft pull request: +:::{figure} img/same-repository/draft-pr.png +:width: 60% +:class: with-border +:alt: Creating a draft pull request +:::: + +Verify that the draft pull request cannot be merged until it is marked as ready +for review: +:::{figure} img/same-repository/draft-pr-wip.png +:width: 60% +:class: with-border +:alt: Draft pull request cannot be merged +:::: + +Draft pull requests can be useful for: +- **Feedback**: You can open a pull request early to get feedback on your work without + signaling that it is ready to merge. +- **Information**: They can help communicating to others that a change is coming up and in + progress. + + +### What is a protected branch? And how to modify it? + +A protected branch on GitHub or GitLab is a branch that cannot (accidentally) +deleted or force-pushed to. It is also possible to require that a branch cannot +be directly pushed to or modified, but that changes must be submitted via a +pull request. + +To protect a branch in your own repository, go to "Settings" -> "Branches". + + + +### Summary + +- We used all the same pieces that we've learned previously. +- But we successfully contributed to a collaborative project! +- The pull request allowed us to contribute without changing directly: + this is very good when it's not mainly our project. diff --git a/_sources/credit.md.txt b/_sources/credit.md.txt new file mode 100644 index 0000000..9639997 --- /dev/null +++ b/_sources/credit.md.txt @@ -0,0 +1,10 @@ +# Credit + +The following material (all CC-BY) was reused to create this workshop material: +- +- +- +- +- + +(I will also summarize what changes have been made to the original material) diff --git a/_sources/documentation.md.txt b/_sources/documentation.md.txt new file mode 100644 index 0000000..571c5c3 --- /dev/null +++ b/_sources/documentation.md.txt @@ -0,0 +1,384 @@ +(documentation)= + +# Code documentation + +:::{objectives} +- Discuss what makes good documentation. +- Improve the README of your project or our example project. +- Explore Sphinx which is a popular tool to build documentation websites. +- Learn how to leverage GitHub Actions and GitHub Pages to build and deploy documentation. +::: + +:::{instructor-note} +- (30 min) Discussion +- (30 min) Exercise: Set up a Sphinx documentation and add API documentation +- (15 min) Demo: Building documentation with GitHub Actions +::: + + +## Why? 💗✉️ to your future self + +- You will probably use your code in the future and may forget details. +- You may want others to use your code or contribute + (almost impossible without documentation). + + +## In-code documentation + +Not very useful (more commentary than comment): +```python +# now we check if temperature is below -50 +if temperature < -50: + print("ERROR: temperature is too low") +``` + +More useful (explaining **why**): +```python +# we regard temperatures below -50 degrees as measurement errors +if temperature < -50: + print("ERROR: temperature is too low") +``` + +Keeping zombie code "just in case" (rather use version control): +```python +# do not run this code! +# if temperature > 0: +# print("It is warm") +``` + +Emulating version control: +```python +# John Doe: threshold changed from 0 to 15 on August 5, 2013 +if temperature > 15: + print("It is warm") +``` + + +## Many languages allow "docstrings" + +Example (Python): +```python +def kelvin_to_celsius(temp_k: float) -> float: + """ + Converts temperature in Kelvin to Celsius. + + Parameters + ---------- + temp_k : float + temperature in Kelvin + + Returns + ------- + temp_c : float + temperature in Celsius + """ + assert temp_k >= 0.0, "ERROR: negative T_K" + + temp_c = temp_k - 273.15 + + return temp_c +``` + +:::{keypoints} +- Documentation which is only in the source code is not enough. +- Often a README is enough. +- Documentation needs to be kept + **in the same Git repository** as the code since we want it to evolve with + the code. +::: + + +## Often a README is enough - checklist + +- **Purpose** +- Requirements +- Installation instructions +- **Copy-paste-able example to get started** +- Tutorials covering key functionality +- Reference documentation (e.g. API) covering all functionality +- Authors and **recommended citation** +- License +- Contribution guide + +See also the +[JOSS review checklist](https://joss.readthedocs.io/en/latest/review_checklist.html). + + +## What if you need more than a README? + +- Write documentation in + [Markdown (.md)](https://en.wikipedia.org/wiki/Markdown) + or + [reStructuredText (.rst)](https://en.wikipedia.org/wiki/ReStructuredText) + or + [R Markdown (.Rmd)](https://rmarkdown.rstudio.com/) + +- In the **same repository** as the code -> version control and **reproducibility** + +- Use one of many tools to build HTML out of md/rst/Rmd: + [Sphinx](https://sphinx-doc.org), + [MkDocs](https://www.mkdocs.org/), + [Zola](https://www.getzola.org/), [Jekyll](https://jekyllrb.com/), + [Hugo](https://gohugo.io/), RStudio, [knitr](https://yihui.org/knitr/), + [bookdown](https://bookdown.org/), + [blogdown](https://bookdown.org/yihui/blogdown/), ... + +- Deploy the generated HTML to [GitHub Pages](https://pages.github.com/) or + [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) + + +## Diátaxis + +Diátaxis is a systematic approach to technical documentation authoring. + +- Overview: +- How to use Diátaxis **as a guide** to work: + + +## Exercise: Set up a Sphinx documentation + +:::{prereq} Preparation +In this episode we will use the following 5 packages which we installed +previously as part of the {ref}`conda` or {ref}`venv`: +``` +myst-parser +sphinx +sphinx-rtd-theme +sphinx-autoapi +sphinx-autobuild +``` + +Which repository to use? You have 3 options: +- Clone **your fork** of the planets example repository. +- If you don't have that, you can clone the original repository itself: + +- You can try this with **your own project** and the project does not have to + be a Python project. +::: + +There are at least two ways to get started with Sphinx: +1. Use `sphinx-quickstart` to create a new Sphinx project. +1. **This is what we will do**: Create three files (`doc/conf.py`, `doc/index.md`, and `doc/about.md`) + as starting point and improve from there. + +::::{exercise} Exercise: Set up a Sphinx documentation +1. Create the following three files in your project: + ``` + planets/ + ├── doc/ + │ ├── conf.py + │ ├── index.md + │ └── about.md + └── ... + ``` + + This is `conf.py`: + ```python + project = "planets" + copyright = "2024, Authors" + author = "Authors" + release = "0.1" + + exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + extensions = [ + "myst_parser", # in order to use markdown + ] + + myst_enable_extensions = [ + "colon_fence", # ::: can be used instead of ``` for better rendering + ] + + html_theme = "sphinx_rtd_theme" + ``` + + This is `index.md` (feel free to adjust): + ```markdown + # Our code documentation + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + + :::{toctree} + :maxdepth: 2 + :caption: Some caption + + about.md + ::: + ``` + + This is `about.md` (feel free to adjust): + ```markdown + # About this code + + Work in progress ... + ``` + +1. Run `sphinx-build` to build the HTML documentation: + ```console + $ sphinx-build doc _build + + ... lots of output ... + The HTML pages are in _build. + ``` + +1. Try to open `_build/index.html` in your browser. + +1. Experiment with adding more content, images, equations, code blocks, ... + - [typography](https://myst-parser.readthedocs.io/en/latest/syntax/typography.html) + - [images](https://myst-parser.readthedocs.io/en/latest/syntax/images_and_figures.html) + - [math and equations](https://myst-parser.readthedocs.io/en/latest/syntax/math.html) + - [code blocks](https://myst-parser.readthedocs.io/en/latest/syntax/code_and_apis.html) +:::: + +There is a lot more you can do: +- This is useful if you want to check the integrity of all internal and external links: + ```console + $ sphinx-build doc -W -b linkcheck _build + ``` +- [sphinx-autobuild](https://pypi.org/project/sphinx-autobuild/) + provides a local web server that will automatically refresh your view + every time you save a file - which makes writing and testing much easier. + + +## Demo: Building documentation with GitHub Actions + +First we need to extend the `environment.yml` file to include the necessary packages: +```{code-block} yaml +--- +emphasize-lines: 9-12 +--- +name: planets +channels: + - conda-forge +dependencies: + - python=3.12 + - numpy + - click + - matplotlib + - myst-parser + - sphinx + - sphinx-rtd-theme + - sphinx-autoapi +``` + +Then we add a GitHub Actions workflow `.github/workflow/sphinx.yml` to build the documentation: +```{code-block} yaml +--- +emphasize-lines: 31 +--- +name: Build documentation + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases + environment-file: environment.yml + init-shell: bash + cache-environment: true + post-cleanup: 'all' + generate-run-shell: false + + - name: Sphinx build + run: | + sphinx-build doc _build + shell: bash -el {0} + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/ + force_orphan: true +``` + +Now: +- Add these two changes to the GitHub repository. +- Go to "Settings" -> "Pages" -> "Branch" -> `gh-pages` -> "Save". +- Look at "Actions" tab and observe the workflow running and hopefully + deploying the website. +- Finally visit the generated site. You find it by clicking the About wheel + icon on top right of your repository. There, select "Use your GitHub Pages + website". +- **This is how we build almost all of our lesson websites** (including this one). +- Another popular place to deploy Sphinx documentation is [ReadTheDocs](https://readthedocs.org/). + + +## Optional: How to auto-generate API documentation in Python + +Add three tiny modifications (highlighted) to `doc/conf.py` to auto-generate API documentation +(this requires the `sphinx-autoapi` package): +```{code-block} python +--- +emphasize-lines: 10, 14, 17 +--- +project = "planets" +copyright = "2024, Authors" +author = "Authors" +release = "0.1" + +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +extensions = [ + "myst_parser", # in order to use markdown + "autoapi.extension", # in order to use markdown +] + +# search this directory for Python files +autoapi_dirs = [".."] + +# ignore this file when generating API documentation +autoapi_ignore = ["*/conf.py"] + +myst_enable_extensions = [ + "colon_fence", # ::: can be used instead of ``` for better rendering +] + +html_theme = "sphinx_rtd_theme" +``` + +Then rebuild the documentation (or push the changes and let GitHub rebuild it) +and you should see a new section "API Reference". + + +## Confused about reStructuredText vs. Markdown vs. MyST? + +- At the beginning there was reStructuredText and Sphinx was built for reStructuredText. +- Independently, Markdown was invented and evolved into a couple of flavors. +- Markdown became more and more popular but was limited compared to reStructuredText. +- Later, [MyST](https://myst-parser.readthedocs.io/en/latest/syntax/typography.html) + was invented to be able to write + something that looks like Markdown but in addition can do everything that + reStructuredText can do with extra directives. + + +## Where to read more + +- [CodeRefinery documentation lesson](https://coderefinery.github.io/documentation/) +- [Sphinx documentation](https://www.sphinx-doc.org/) +- [Sphinx + ReadTheDocs guide](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/index.html) +- For more Markdown functionality, see the [Markdown guide](https://www.markdownguide.org/basic-syntax/). +- For Sphinx additions, see [Sphinx Markup Constructs](https://www.sphinx-doc.org/en/master/markup/index.html). +- [An opinionated guide on documentation in Python](https://docs.python-guide.org/writing/documentation/) diff --git a/_sources/example.md.txt b/_sources/example.md.txt new file mode 100644 index 0000000..9805632 --- /dev/null +++ b/_sources/example.md.txt @@ -0,0 +1,95 @@ +(example-project)= + +# Example project: Simulating the motion of planets + +The [example code](https://github.com/workshop-material/planets) that we will study +is a hopefully simple N-body simulation written in Python. It is not important +or expected that we understand the code in any detail. + +:::{video} video/animation.mp4 +:width: 600 +::: + +The **big picture** is that the code simulates the motion of a number of +planets: +- We can choose the number of planets. +- Each planet starts with a random position, velocity, and mass. +- At each time step, the code calculates the gravitational force between each + pair of planets. +- The forces accelerate each planet, the acceleration modifies the velocity, + the velocity modifies the position of each planet. +- We can choose the number of time steps. +- The units were chosen to make numbers easy to read. + + +## Example run + +:::{instructor-note} +The instructor demonstrates running the code on their computer. +::: + +The code is written to accept **command-line arguments** to specify the number +of planets and the number of time steps. + +We first generate starting data: +```console +$ python generate-data.py --num-planets 10 --output-file initial.csv +``` + +The generated file (initial.csv) could look like this: +``` +px,py,pz,vx,vy,vz,mass +-46.88,-42.51,88.33,-0.86,-0.18,0.55,6.70 +-5.29,17.09,-96.13,0.66,0.45,-0.17,3.51 +83.53,-92.83,-68.77,-0.26,-0.48,0.24,6.84 +-36.31,25.48,64.16,0.85,0.75,-0.56,1.53 +-68.38,-17.21,-97.07,0.60,0.26,0.69,6.63 +-48.37,-48.74,3.92,-0.92,-0.33,-0.93,8.60 +40.53,-75.50,44.18,-0.62,-0.31,-0.53,8.04 +-27.21,10.78,-78.82,-0.09,-0.55,-0.03,5.35 +88.42,-74.95,-45.85,0.81,0.68,0.56,5.36 +39.09,53.12,-59.54,-0.54,0.56,0.07,8.98 +``` + +Then we can simulate their motion (in this case for 20 steps): +```console +$ python simulate.py --num-steps 20 \ + --input-file initial.csv \ + --output-file final.csv +``` + +The `--output-file` (final.csv) is again a CSV file (comma-separated values) +and contains the final positions of all planets. + +It is possible to run on **multiple cores** and to **animate** the result. +Here is an example with 100 planets: +```{code-block} console +--- +emphasize-lines: 7,11 +--- +$ python generate-data.py --num-planets 100 --output-file initial.csv + +$ python simulate.py --num-steps 50 \ + --input-file initial.csv \ + --output-file final.csv \ + --trajectories-file trajectories.npz \ + --num-cores 8 + +$ python animate.py --initial-file initial.csv \ + --trajectories-file trajectories.npz \ + --output-file animation.mp4 +``` + +:::{admonition} Learning goals +- What are the most important steps to make this code **reusable by others** + and **our future selves**? +- Be able to apply these techniques to your own code/script. +::: + +:::{admonition} We will not focus on ... +- ... how the code works internally in detail. +- ... whether this is the most efficient algorithm. +- ... whether the code is numerically stable. +- ... how to code scales with the number of cores. +- ... whether it is portable to other operating systems (we will discuss this later). +::: diff --git a/_sources/index.md.txt b/_sources/index.md.txt new file mode 100644 index 0000000..647b711 --- /dev/null +++ b/_sources/index.md.txt @@ -0,0 +1,142 @@ +# Reproducible research software development using Python + + +## Big-picture goal + +This is a **hands-on course on research software engineering**. In this +workshop we assume that most workshop participants use Python in their work or +a leading a group which uses Python. Therefore, some of the examples will use +Python as the example language. + +We will work with an example project and go through all the steps of a typical +software project. Once we have seen the building blocks, we will try to apply +them to own projects. Workshop participants will receive and also learn to give +constructive code feedback. + + +## Prerequisites + +:::{prereq} Preparation +1. Get a **GitHub account** following [these instructions](https://coderefinery.github.io/installation/github/). +1. You will need a **text editor**. If you don't have a favorite one, we recommend + [VS Code](https://coderefinery.github.io/installation/vscode/). +1. **If you prefer to work in the terminal** and not in VS Code, set up these two (skip this if you use VS Code): + - [Git in the terminal](https://coderefinery.github.io/installation/git-in-terminal/) + - [SSH or HTTPS connection to GitHub from terminal](https://coderefinery.github.io/installation/ssh/) +1. **One of these two software environments** (if you are not sure which one to + choose or have no preference, choose Conda): + - {ref}`conda` + - {ref}`venv` (Snakemake is not available in this environment) +1. **Optional** and only on Linux: [Apptainer](https://apptainer.org/) following + [these instructions](https://apptainer.org/docs/admin/1.3/installation.html#install-from-pre-built-packages). +::: + + +## Schedule + +### Day 1 + +- 13:00-13:30 - **Welcome and introduction** + - Practical information (tools, communication, breaks, etc.) + - Motivation (reproducibility, robustness, distribution, improvement, trust, etc.) + - What will learn and achieve from this course? + - {ref}`example-project` + +- 13:30-14:45 - {ref}`version-control` (1/2) + - {ref}`version-control-motivation` (15 min) + - {ref}`browsing` (30 min) + - {ref}`branching-and-committing` (30 min) + +- 15:00-16:30 - {ref}`version-control` (2/2) + - Merging (40 min) + - Conflict resolution (30 min) + - {ref}`practical-advice` (20 min) + - {ref}`sharing-repositories` (30 min) + +- 16:45-18:00 - {ref}`documentation` + + +### Day 2 + +- 09:00-10:30 - {ref}`collaboration` (1/2) + - {ref}`concepts` (10 min) + - {ref}`same-repository` (40 min) + - {ref}`code-review` (40 min) + +- 10:45-12:15 - {ref}`collaboration` (2/2) + - {ref}`forking-workflow` (50 min) + - {ref}`collaboration-demo-discussion` (40 min) + +- 16:45-18:00 - **Debriefing and Q&A** + - Participants work on their projects + - Together we study actual codes that participants wrote or work on + - Constructively we discuss possible improvements + - Give individual feedback on code projects + + +### Day 3 + +- 09:00-10:30 - {ref}`testing` + +- 10:45-12:15 - {ref}`reusable` + - Tracking dependencies with requirements.txt and environment.yml + - Recording environments in containers + +- 13:00-14:45 - Code quality and good practices + - {ref}`refactoring-demo` (90 min) + - {ref}`refactoring-concepts` (15 min) + +- 15:00-16:30 - {ref}`publishing` + - Licenses + - Publishing the code via Zenodo + - Packaging the code + - Sharing the code via PyPI + +- 16:45-18:00 - **Debriefing and Q&A** + - Participants work on their projects + - Together we study actual codes that participants wrote or work on + - Constructively we discuss possible improvements + - Give individual feedback on code projects + + +### If we have time left + +- {ref}`profiling` + + +```{toctree} +:maxdepth: 1 +:caption: Software environment +:hidden: + +installation/conda +installation/virtual-environment +``` + +```{toctree} +:maxdepth: 1 +:caption: Episodes +:hidden: + +example +version-control +documentation +collaboration +testing +reusable +refactoring-demo +refactoring-concepts +publishing +profiling +``` + +```{toctree} +:maxdepth: 1 +:caption: Reference +:hidden: + +credit +All lessons +CodeRefinery +Reusing +``` diff --git a/_sources/installation/conda.md.txt b/_sources/installation/conda.md.txt new file mode 100644 index 0000000..830161f --- /dev/null +++ b/_sources/installation/conda.md.txt @@ -0,0 +1,82 @@ +(conda)= + +# Conda environment + +A Conda environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located. + +You will need a `environment.yml` file that documents the dependencies: +```yaml +name: coderefinery +channels: + - conda-forge + - bioconda +dependencies: + - python >= 3.10 + - black + - click + - flit + - ipywidgets + - isort + - jupyterlab + - jupyterlab_code_formatter + - jupyterlab-git + - matplotlib + - myst-parser + - nbdime + - numpy + - pandas + - pytest + - pytest-cov + - scalene + - seaborn + - snakemake-minimal + - sphinx + - sphinx-autoapi + - sphinx-autobuild + - sphinx_rtd_theme >= 2.0 + - vulture + - scikit-image +``` + + +## Before you create a virtual environment + +1. Create a new directory for this course. +1. In this directory, create an `environment.yml` file and copy-paste the dependencies above into it. + + +## Choose the tool to manage the environment + +If you are already using one of these tools, please continue using the tool that you like and know. +If you are new to this, **we recommend using Miniconda or Miniforge**. + +- [Anaconda](https://docs.anaconda.com/anaconda/install/) + - Advantages: easy to install, easy to use, good for beginners + - Disadvantages: large download, installs more than we will need, license restrictions +- [Miniconda](https://docs.anaconda.com/miniconda/) + - Advantages: small size, installs only what you need + - Disadvantages: no graphical interface, license restrictions +- [Miniforge](https://github.com/conda-forge/miniforge) + - Advantages: small size, no license restrictions + - Disadvantages: no graphical interface +- [Micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html) + - Advantages: fast, small size + - Disadvantages: no graphical interface +- [Pixi](https://pixi.sh/latest/) + - Advantages: fast and new + - Disadvantages: new and less tested and not documented here + + +## Creating the virtual environment + +1. Open your terminal shell (e.g. Bash or Zsh). +2. Activate `conda` using `conda activate` or `source ~/miniconda3/bin/activate`. +3. Run the following command: + ```console + $ conda env create --file environment.yml + ``` +4. Make sure that you see "coderefinery" in the output when you ask for a list of all available environments: + ```console + $ conda env list + ``` diff --git a/_sources/installation/virtual-environment.md.txt b/_sources/installation/virtual-environment.md.txt new file mode 100644 index 0000000..69b3231 --- /dev/null +++ b/_sources/installation/virtual-environment.md.txt @@ -0,0 +1,77 @@ +(venv)= + +# Virtual environment + +A virtual environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located. + +You will need a `requirements.txt` file that documents the dependencies: +``` +black +click +flit +ipywidgets +isort +jupyterlab +jupyterlab-code-formatter +jupyterlab-git +matplotlib +myst-parser +nbdime +numpy +pandas +pytest +pytest-cov +scalene +seaborn +sphinx +sphinx-autoapi +sphinx-autobuild +sphinx_rtd_theme >= 2.0 +vulture +scikit-image +``` + + +## Before you create a virtual environment + +1. Create a new directory for this course. +1. In this directory, create a `requirements.txt` file and copy-paste the dependencies above into it. + + +## Creating the virtual environment + +Now create a virtual environment in this directory either using [pip and +venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/) +(more traditional and safer) or using [uv](https://docs.astral.sh/uv/) (more modern but also less tested): + +:::::{tabs} + +::::{group-tab} pip and venv +Create a new virtual environment and activate it: +```console +$ python3 -m venv coderefinery-environment +$ source coderefinery-environment/bin/activate +``` + +Install the dependencies into the environment: +```console +$ python3 -m pip install -r requirements.txt +``` +:::: + + +::::{group-tab} uv +Create a new virtual environment and activate it: +```console +$ uv venv coderefinery-environment +$ source coderefinery-environment/bin/activate +``` + +Install the dependencies into the environment: +```console +$ uv pip install -r requirements.txt +``` +:::: + +::::: diff --git a/_sources/profiling.md.txt b/_sources/profiling.md.txt new file mode 100644 index 0000000..0683e06 --- /dev/null +++ b/_sources/profiling.md.txt @@ -0,0 +1,16 @@ +(profiling)= + +# Profiling memory and CPU usage + +Profilers: +- cProfile or profile +- line_profiler +- viztracer +- scalene +- Intel VTune +- PyInstrument +- py-spy +- Pyflame +- timeit + +(here I will add some examples that we can profile together) diff --git a/_sources/publishing.md.txt b/_sources/publishing.md.txt new file mode 100644 index 0000000..bc3a12e --- /dev/null +++ b/_sources/publishing.md.txt @@ -0,0 +1,11 @@ +(publishing)= + +# How to release and publish your code + +Part 1: +- 30 min: Overview over software licenses and code publishing +- 15 min: We add a license, CFF file, and publish to Zenodo (sandbox) + +Part 2: +- 30 min: Creating a Python package with the help of flit +- 15 min: Deploy the package to test-PyPI using GitHub Actions diff --git a/_sources/refactoring-concepts.md.txt b/_sources/refactoring-concepts.md.txt new file mode 100644 index 0000000..78039ad --- /dev/null +++ b/_sources/refactoring-concepts.md.txt @@ -0,0 +1,8 @@ +(refactoring-concepts)= + +# Concepts in refactoring and modular code design + +Topics: +- Pure functions +- Design patterns: functional design vs. object-oriented design +- How to design your code before writing it: document-driven development diff --git a/_sources/refactoring-demo.md.txt b/_sources/refactoring-demo.md.txt new file mode 100644 index 0000000..e7266bf --- /dev/null +++ b/_sources/refactoring-demo.md.txt @@ -0,0 +1,115 @@ +(refactoring-demo)= + +# Demo: From a script towards a workflow + +In this episode we will explore code quality and good practices in Python using +a hands-on approach. We will together build up a small project and improve it +step by step. + +We will start from a relatively simple image processing script which can read a +telescope image of stars and our goal is to **count the number of stars** in +the image. Later we will want to be able to process many such images. + +The (fictional) telescope images look like the one below here ([in this +repository](https://github.com/workshop-material/random-star-images) we can find more): +:::{figure} refactoring/stars.png +:alt: Generated image representing a telescope image of stars +:width: 60% + +Generated image representing a telescope image of stars. +::: + +:::{admonition} Rough plan for this demo +- (15 min) Discuss how we would solve the problem, run example code, and make it work (as part of a Jupyter notebook)? +- (15 min) Refactor the positioning code into a function and a module +- (15 min) Now we wish to process many images - discuss how we would approach this +- (15 min) Introduce CLI and discuss the benefits +- (30 min) From a script to a workflow (using Snakemake) +::: + +:::{solution} Starting point (spoiler alert) + +We can imagine that we pieced together the following code +based on some examples we found online: +```python +import matplotlib.pyplot as plt +from skimage import io, filters, color +from skimage.measure import label, regionprops + + +image = io.imread("stars.png") +sigma = 0.5 + +# if there is a fourth channel (alpha channel), ignore it +rgb_image = image[:, :, :3] +gray_image = color.rgb2gray(rgb_image) + +# apply a gaussian filter to reduce noise +image_smooth = filters.gaussian(gray_image, sigma) + +# threshold the image to create a binary image (bright stars will be white, background black) +thresh = filters.threshold_otsu(image_smooth) +binary_image = image_smooth > thresh + +# label connected regions (stars) in the binary image +labeled_image = label(binary_image) + +# get properties of labeled regions +regions = regionprops(labeled_image) + +# extract star positions (centroids) +star_positions = [region.centroid for region in regions] + +# plot the original image +plt.figure(figsize=(8, 8)) +plt.imshow(image, cmap="gray") + +# overlay star positions with crosses +for star in star_positions: + plt.plot(star[1], star[0], "rx", markersize=5, markeredgewidth=0.1) + +plt.savefig("detected-stars.png", dpi=300) + +print(f"number of stars detected: {len(star_positions)}") +``` +::: + + +## Plan + +Topics we wish to show and discuss: +- Naming (and other) conventions, project organization, modularity +- The value of pure functions and immutability +- Refactoring (explained through examples) +- Auto-formatting and linting with tools like black, vulture, ruff +- Moving a project under Git +- How to document dependencies +- Structuring larger software projects in a modular way +- Command-line interfaces +- Workflows with Snakemake + +We will work together on the code on the big screen, and participants will be +encouraged to give suggestions and ask questions. We will **end up with a Git +repository** which will be shared with workshop participants. + + +## Possible solutions + +:::{solution} Script after some work, with command-line interface (spoiler alert) +This is one possible solution (`count-stars.py`): +```{literalinclude} refactoring/count-stars.py +:language: python +``` +::: + +:::{solution} Snakemake rules which define a workflow (spoiler alert) +This is one possible solution (`snakefile`): +```{literalinclude} refactoring/snakefile +:language: python +``` + +We can process as many images as we like by running: +```console +$ snakemake --cores 4 # adjust to the number of available cores +``` +::: diff --git a/_sources/reusable.md.txt b/_sources/reusable.md.txt new file mode 100644 index 0000000..c86455a --- /dev/null +++ b/_sources/reusable.md.txt @@ -0,0 +1,13 @@ +(reusable)= + +# How to make the project more reusable + +Part 1: +- 15 min: Motivation +- 15 min: Best practices for managing dependencies in Python +- 20 min: Exercise + +Part 2: +- 10 min: Introduction to containers +- 20 min: Demonstration +- 10 min: Discussion diff --git a/_sources/testing.md.txt b/_sources/testing.md.txt new file mode 100644 index 0000000..da43e9f --- /dev/null +++ b/_sources/testing.md.txt @@ -0,0 +1,222 @@ +(testing)= + +# Automated testing + +:::{objectives} +- Know **where to start** in your own project. +- Have an example for how to make the **testing part of code review**. +::: + +:::{instructor-note} +- (15 min) Motivation +- (15 min) End-to-end tests +- (15 min) Pytest +- (15 min) Adding the unit test to GitHub Actions +- (10 min) What else is possible +- (20 min) Exercise +::: + + +## Motivation + +Testing is a way to check that the code does what it is expected to. + +- **Less scary to change code**: tests will tell you whether something broke. +- **Easier for new people** to join. +- Easier for somebody to **revive an old code**. +- **End-to-end test**: run the whole code and compare result to a reference. +- **Unit tests**: test one unit (function or module). Can guide towards better + structured code: complicated code is more difficult to test. + + +## How testing is often taught + +```python +def add(a, b): + return a + b + + +def test_add(): + assert add(1, 2) == 3 +``` + +How this feels: +:::{figure} img/owl.png +:alt: Instruction on how to draw an owl +:width: 50% +:class: with-border + +[Citation needed] +::: + +Instead, we will look at and **discuss a real example** where we test components +from our example project. + + +## Where to start + +Short answer: **Start with an end-to-end test**. + +:::{solution} Longer answer +- A simple script or notebook probably does not need an automated test. + +**If you have nothing yet**: +- Start with an end-to-end test. +- Describe in words how *you* check whether the code still works. +- Translate the words into a script (any language). +- Run the script automatically on every code change. + +**If you want to start with unit-testing**: +- You want to rewrite a function? Start adding a unit test right there first. +::: + + +## End-to-end tests + +- This is our end-to-end test: +- Note how we can run it [on GitHub automatically](https://github.com/workshop-material/planets/blob/813d49a247f36e9c1e10cbe78ecf1ae4b6e971c3/.github/workflows/test.yml#L28). +- Also browse . +- If we have time, we can try to create a pull request which would break the + code and see how the test fails. + +:::{discussion} +Is the [end-to-end test](https://github.com/workshop-material/planets/blob/main/test.sh) +perfect? No. But it's a good starting point. Discuss its limitations. +::: + + +## Pytest + +First we need to add a test function, for instance +for [this function](https://github.com/workshop-material/planets/blob/813d49a247f36e9c1e10cbe78ecf1ae4b6e971c3/simulate.py#L31-L39): +```{code-block} python +--- +emphasize-lines: 12-20 +--- +def force_between_planets(position1, mass1, position2, mass2): + G = 1.0 # gravitational constant + + r = position2 - position1 + distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5 + force_magnitude = G * mass1 * mass2 / distance**2 + force = (r / distance) * force_magnitude + + return force + + +def test_force_between_planets(): + position1 = np.array([0.0, 0.0, 0.0]) + mass1 = 1.0 + position2 = np.array([1.0, 0.0, 0.0]) + mass2 = 2.0 + + force = force_between_planets(position1, mass1, position2, mass2) + + assert np.allclose(force, [2.0, 0.0, 0.0]) +``` + +Let us run the test with: +```console +$ pytest simulate.py +``` + +Explanation: `pytest` will look for functions starting with `test_` in files +and directories given as arguments. It will run them and report the results. + +Now let us try this: +- Commit the test. +- Break the function on purpose and run the test. +- Does the test fail as expected? + + +## Adding the unit test to GitHub Actions + +Our next goal is that we want GitHub to run the unit test +automatically on every change. + +First we need to extend our +[environment.yml](https://github.com/workshop-material/planets/blob/main/environment.yml): +```{code-block} yaml +--- +emphasize-lines: 9 +--- +name: planets +channels: + - conda-forge +dependencies: + - python=3.12 + - numpy + - click + - matplotlib + - pytest +``` + +We also need to extend `.github/workflows/test.yml` (highlighted line): +```{code-block} yaml +--- +emphasize-lines: 29 +--- +name: Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases + environment-file: environment.yml + init-shell: bash + cache-environment: true + post-cleanup: 'all' + generate-run-shell: false + + - name: Run tests + run: | + ./test.sh + pytest simulate.py + shell: bash -el {0} +``` + +If we have time, we can try to create a pull request which would break the +code and see how the test fails. + + +## What else is possible + +- The testing above used **example-based** testing. + +- **Test coverage**: how much of the code is traversed by tests? + - Python: [pytest-cov](https://pytest-cov.readthedocs.io/) + - Result can be deployed to services like [Codecov](https://about.codecov.io/) or [Coveralls](https://coveralls.io/). + +- **Property-based** testing: generates arbitrary data matching your specification and checks that your guarantee still holds in that case. + - Python: [hypothesis](https://hypothesis.readthedocs.io/) + +- **Snapshot-based** testing: makes it easier to generate snapshots for regression tests. + - Python: [syrupy](https://syrupy-project.github.io/syrupy/) + +- **Mutation testing**: tests pass -> change a line of code (make a mutant) -> test again and check whether all mutants get "killed". + - Python: [mutmut](https://mutmut.readthedocs.io/) + + +## Exercises + +Experiment with the example project and what we learned above or try it on your +own project: +- Add a unit test. +- Try to run it locally. +- Check whether it fails when you break the corresponding function. +- Try to run it on GitHub Actions. +- Create a pull request which would break the code and see whether the automatic test would catch it. +- Try to design an end-to-end test for your project. diff --git a/_sources/version-control.md.txt b/_sources/version-control.md.txt new file mode 100644 index 0000000..efcfece --- /dev/null +++ b/_sources/version-control.md.txt @@ -0,0 +1,13 @@ +(version-control)= + +# Introduction to version control with Git and GitHub + +```{toctree} +:maxdepth: 1 + +version-control/motivation.md +version-control/browsing.md +version-control/branching-and-committing.md +version-control/practical-advice.md +version-control/sharing.md +``` diff --git a/_sources/version-control/branching-and-committing.md.txt b/_sources/version-control/branching-and-committing.md.txt new file mode 100644 index 0000000..702e712 --- /dev/null +++ b/_sources/version-control/branching-and-committing.md.txt @@ -0,0 +1,339 @@ +(branching-and-committing)= + +# Creating branches and commits + +The first and most basic task to do in Git is **record changes** using +commits. In this part, we will record changes in two +ways: on a **new branch** (which supports multiple lines of work at once), and directly +on the "main" branch (which happens to be the default branch here). + +:::{objectives} +- Record new changes to our own copy of the project. +- Understand adding changes in two separate branches. +- See how to compare different versions. +::: + + +## Background + +- In the previous episode we have browsed an existing **repository** and saw **commits** + and **branches**. +- Each **commit** is a snapshot of the entire project at a certain + point in time and has a unique identifier (**hash**) . +- A **branch** is a line of development, and the `main` branch or `master` branch + are often the default branch in Git. +- A branch in Git is like a **sticky note that is attached to a commit**. When we add + new commits to a branch, the sticky note moves to the new commit. +- **Tags** are a way to mark a specific commit as important, for example a release + version. They are also like a sticky note, but they don't move when new + commits are added. + +:::{figure} img/gophers.png +:alt: Branching explained with a gopher +:width: 100% + +What if two people, at the same time, make two different changes? Git +can merge them together easily. Image created using +([inspiration](https://twitter.com/jay_gee/status/703360688618536960)). +::: + + +## Exercise: Creating branches and commits + +:::{figure} img/illustrations/branches.png +:alt: Illustration of what we want to achieve in this exercise +:width: 60% + +Illustration of what we want to achieve in this exercise. +::: + +:::{exercise} Exercise: Practice creating commits and branches (20 min) +1. First create a new branch and then either add a new file or modify an + existing file and commit the change. Make sure that you now work **on your + copy** of the example repository. In your commit you can share a Git or + programming trick you like. +1. In a new commit, modify the file again. +1. Switch to the `main` branch and create a commit there. +1. Browse the network and locate the commits that you just created ("Insights" -> "Network"). +1. Compare the branch that you created with the `main` branch. Can you find an easy way to see the differences? +1. Can you find a way to compare versions between two arbitrary commits in the repository? +1. Try to rename the branch that you created and then browse the network again. +1. Try to create a tag for one of the commits that you created (on GitHub, + create a "release"). +::: + +The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren't enough - this is by +design. + + +## Solution and walk-through + + +### (1) Create a new branch and a new commit + +:::::{tabs} +::::{group-tab} GitHub +1. Where it says "main" at the top left, click, enter a new branch + name `new-tutorial`, click on the offer to create the new branch + ("Create branch new-tutorial from main"). +1. Make sure you are still on the `new-tutorial` branch (it should say + it at the top), and click "Add file" → "Create new file" from the + upper right. +1. Enter a filename where it says "Name your file...". +1. Share some Git or programming trick you like. +1. Click "Commit changes" +1. Enter a commit message. Then click "Commit + changes". + +You should appear back at the file browser view, and see your +modification there. +:::: + +::::{group-tab} VS Code +1. Make sure that you are on the main branch. +1. Version control button on left sidebar → Three dots in upper right of source control → Branch → Create branch. +1. VS Code automatically switches to the new branch. +4. Create a new file. +4. In the version control sidebar, click the `+` sign to add the file for the next commit. +4. Enter a brief message and click "Commit". +:::: + +::::{group-tab} Command line +Create a new branch called `new-tutorial` from `main` and switch to it: +```console +$ git switch --create new-tutorial main +``` + +Then create the new file. Finally add and commit the file: +```console +$ git add tutorial.md # or a different file name +$ git commit -m "sharing a programming trick" +``` +:::: +::::: + + +### (2) Modify the file again with a new commit + +:::::{tabs} +::::{group-tab} GitHub +This is similar to before, but we click on the existing file to +modify. + +1. Click on the file you added or modified previously. +2. Click the edit button, the pencil icon at top-right. +3. Follow the "Commit changes" instructions as in the previous step. +:::: + +::::{group-tab} VS Code +Repeat as in the previous step. +:::: + +::::{group-tab} Command line +Modify the file. Then commit the new change: +```console +$ git add tutorial.md +$ git commit -m "short summary of the change" +``` + +Make sure to replace "short summary of the change" with a meaningful commit +message. +:::: +::::: + + +### (3) Switch to the main branch and create a commit there + +:::::{tabs} +::::{group-tab} GitHub +1. Go back to the main repository page (your user's page). +1. In the branch switch view (top left above the file view), switch to + `main`. +1. Modify another file that already exists, following the pattern + from above. +:::: + +::::{group-tab} VS Code +Use the branch selector at the bottom to switch back to the main branch. Repeat the same steps as above, +but this time modify a different file. +:::: + +::::{group-tab} Command line +First switch to the `main` branch: +```console +$ git switch main +``` + +Then modify a file. Finally `git add` and then commit the change: +```console +$ git commit -m "short summary of the change" +``` +:::: +::::: + + +### (4) Browse the commits you just made + +Let's look at what we did. Now, the `main` and `new-tutorial` branches +have diverged: both have some modifications. Try to find the commits +you created. + +:::::{tabs} +::::{group-tab} GitHub +Insights tab → Network view (just like we have done before). +:::: + +::::{group-tab} VS Code +This requires an extension. Opening the VS Code terminal lets you use the +command line method (View → Terminal will open a terminal at bottom). This is +a normal command line interface and very useful for work. +:::: + +::::{group-tab} Command line +```console +$ git graph +$ git log --graph --oneline --decorate --all # if you didn't define git graph yet. +``` +:::: +::::: + + +### (5) Compare the branches + +Comparing changes is an important thing we need to do. When using the +GitHub view only, this may not be so common, but we'll show it so that +it makes sense later on. + +:::::{tabs} + +::::{group-tab} GitHub +A nice way to compare branches is to add `/compare` to the URL of the repository, +for example (replace USER): +`https://github.com/USER/planets/compare` +:::: + +::::{group-tab} VS Code +This seems to require an extension. We recommend you use the command line method. +:::: + +::::{group-tab} Command line +```console +$ git diff main new-tutorial +``` + +Try also the other way around: +```console +$ git diff new-tutorial main +``` + +Try also this if you only want to see the file names that are different: +```console +$ git diff --name-only main new-tutorial +``` +:::: +::::: + + +### (6) Compare two arbitrary commits + +This is similar to above, but not only between branches. + +:::::{tabs} +::::{group-tab} GitHub +Following the `/compare`-trick above, one can compare commits on GitHub by +adjusting the following URL: +`https://github.com/USER/planets/compare/VERSION1..VERSION2` + +Replace `USER` with your username and `VERSION1` and `VERSION2` with a commit hash or branch name. +Please try it out. +:::: + +::::{group-tab} VS Code +Again, we recommend using the Command Line method. +:::: + +::::{group-tab} Command line +First try this to get a short overview of the commits: +```console +$ git log --oneline +``` + +Then try to compare any two commit identifiers with `git diff`. +:::: +::::: + + +### (7) Renaming a branch + +:::::{tabs} +::::{group-tab} GitHub + +Branch button → View all branches → three dots at right side → Rename branch. + +:::: +::::{group-tab} VS Code +Version control sidebar → Three dots (same as in step 2) → Branch → Rename branch. Make sure you are on the right branch before you start. +:::: + +::::{group-tab} Command line +Renaming the current branch: +```console +$ git branch -m new-branch-name +``` + +Renaming a different branch: +```console +$ git branch -m different-branch new-branch-name +``` +:::: +::::: + + +### (8) Creating a tag + +Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don't move when new +commits are added. + +:::::{tabs} +::::{group-tab} GitHub +On the right side, below "Releases", click on "Create a new release". + +What GitHub calls releases are actually tags in Git with additional metadata. +For the purpose of this exercise we can use them interchangeably. +:::: + +::::{group-tab} VS Code +Version control sidebar → Three dots (same as in step 2) → Tags → Create tag. Make sure you are on the expected commit before you do this. +:::: + +::::{group-tab} Command line +Creating a tag: +```console +$ git tag -a v1.0 -m "New manuscript version for the pre-print" +``` +:::: +::::: + + +## Summary + +In this part, we saw how we can make changes to our files. With branches, we +can track several lines of work at once, and can compare their differences. + +- You could commit directly to `main` if there is only one single line + of work and it's only you. +- You could commit to branches if there are multiple lines of work at + once, and you don't want them to interfere with each other. +- Tags are useful to mark a specific commit as important, for example a + release version. +- In Git, commits form a so-called "graph". Branches are tags in Git function + like sticky notes that stick to specific commits. What this means for us is + that it does not cost any significant disk space to create new branches. +- Not all files should be added to Git. For example, temporary files or + files with sensitive information or files which are generated as part of + the build process should not be added to Git. For this we use + `.gitignore` (more about this later: {ref}`practical-advice`). +- Unsure on which branch you are or what state the repository is in? + On the command line, use `git status` frequently to get a quick overview. diff --git a/_sources/version-control/browsing.md.txt b/_sources/version-control/browsing.md.txt new file mode 100644 index 0000000..d3c3dc0 --- /dev/null +++ b/_sources/version-control/browsing.md.txt @@ -0,0 +1,374 @@ +(browsing)= + +# Forking, cloning, and browsing + +In this episode, we will look at an **existing repository** to understand how +all the pieces work together. Along the way, we will make a copy (by +**forking** and/or **cloning**) of the repository for us, which will be used +for our own changes. + +:::{objectives} +- See a real Git repository and understand what is inside of it. +- Understand how version control allows advanced inspection of a + repository. +- See how Git allows multiple people to work on the same project at the same time. +- **See the big picture** instead of remembering a bunch of commands. +::: + + +## GitHub, VS Code, or command line + +We offer **three different paths** for this exercise: +- **GitHub** (this is the one we will demonstrate) +- **VS Code** (if you prefer to follow along using an editor) +- **Command line** (for people comfortable with the command line) + + +## Creating a copy of the repository by "forking" or "cloning" + +A **repository** is a collection of files in one directory tracked by Git. A +GitHub repository is GitHub's copy, which adds things like access control, +issue tracking, and discussions. Each GitHub repository is owned by a user or +organization, who controls access. + +First, we need to make **our own copy** of the exercise repository. This will +become important later, when we make our own changes. + +:::{figure} img/illustrations/fork.png +:alt: Illustration of forking a repository on GitHub +:width: 50% + +Illustration of forking a repository on GitHub. +::: + +:::{figure} img/illustrations/clone.png +:alt: Illustration of cloning a repository to your computer +:width: 50% + +Illustration of cloning a repository to your computer. +::: + +:::{figure} img/illustrations/clone-of-fork.png +:alt: Illustration of cloning a forked repository to your computer +:width: 60% + +It is also possible to do this: to clone a forked repository to your computer. +::: + +At all times you should be aware of if you are looking at **your repository** +or the **upstream repository** (original repository): +- Your repository: https://github.com/**USER**/planets +- Upstream repository: https://github.com/**workshop-material**/planets + +:::{admonition} How to create a fork +1. Go to the repository view on GitHub: +1. First, on GitHub, click the button that says "Fork". It is towards + the top-right of the screen. +1. You should shortly be redirected to your copy of the repository + **USER/planets**. +::: + +:::{instructor-note} +Before starting the exercise session show +how to fork the repository to own account +(above). +::: + + + +## Exercise: Copy and browse an existing project + +Work on this by yourself or in pairs. + + + + + + + + + + + + +::::::{prereq} Exercise preparation +:::::{tabs} +::::{group-tab} GitHub +In this case you will work on a fork. + +You only need to open your own view, as described above. The browser +URL should look like `https://github.com/USER/planets`, where +`USER` is your GitHub username. +:::: + +::::{group-tab} VS Code +You need to have forked the repository as described above. + +We need to start by making a clone of this repository so that you can work +locally. + +1. Start VS Code. +1. If you don't have the default view (you already have a project +open), go to File → New Window. +1. Under "Start" on the screen, select "Clone Git Repository...". Alternatively + navigate to the "Source Control" tab on the left sidebar and click on the "Clone Repository" button. +1. Paste in this URL: `https://github.com/USER/planets`, where + `USER` is your username. You can copy this from the browser. +1. Browse and select the folder in which you want to clone the + repository. +1. Say yes, you want to open this repository. +1. Select "Yes, I trust the authors" (the other option works too). +:::: + +::::{group-tab} Command line +**This path is advanced and we do not include all command line +information: you need to be somewhat +comfortable with the command line already.** + +You need to have forked the repository as described above. + +We need to start by making a clone of this repository so that you can work +locally. + +1. Start the terminal in which you use Git (terminal application, or + Git Bash). +1. Change to the directory where you would want the repository to be + (`cd ~/code` for example, if the `~/code` directory is where you + store your files). +1. Run the following command: `git clone + https://github.com/USER/planets`, where `USER` is your + username. You might need to use a SSH clone command instead of + HTTPS, depending on your setup. +1. Change to that directory: `cd planets` +:::: +::::: +:::::: + + +:::{exercise} Exercise: Browsing an existing project (20 min) + +Browse the [example project](https://github.com/workshop-material/planets) and +explore commits and branches, either on a fork or on a clone. Take notes and +prepare questions. The hints are for the GitHub path in the browser. + +1. Browse the **commit history**: Are commit messages understandable? + (Hint: "Commit history", the timeline symbol, above the file list) +1. Compare the commit history with the **network graph** ("Insights" -> "Network"). Can you find the branches? +1. Try to find the **history of commits for a single file**, e.g. `simulate.py`. + (Hint: "History" button in the file view) +1. **Which files include the word "position"**? + (Hint: the GitHub search on top of the repository view) +1. In the `simulate.py` file, find out **who modified each line last and when** + (click on file, then click "Blame" button). Find out who modified the gravitational constant + last and in which commit. + (Hint: "Blame" view in the file view) +1. Can you use this code yourself? **Are you allowed to share + modifications**? + (Hint: look for a license file) +::: + +The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren't enough - this is by +design. + + +## Solution and walk-through + +### (1) Basic browsing + +The most basic thing to look at is the history of commits. + +* This is visible from a button in the repository view. We see every + change, when, and who has committed. +* Every change has a unique identifier, such as `244c993`. This can + be used to identify both this change, and the whole project's + version as of that change. +* Clicking on a change in the view shows more. + +:::::{tabs} + +::::{group-tab} GitHub +Click on the timeline symbol in the repository view: + :::{figure} img/browsing/history.png + :alt: Screenshot on GitHub of where to find the commit history + :width: 100% + :class: with-border + ::: +:::: + +::::{group-tab} VS Code +This can be done from "Timeline", in the bottom of explorer, but only +for a single file. +:::: + +::::{group-tab} Command line +Run: +```console +$ git log +``` + +Try also: +```console +$ git log --oneline +``` +:::: + +::::: + + +### (2) Compare commit history with network graph + +The commit history we saw above looks linear: one commit after +another. But if we look at the network view, we see some branches and +merges. We'll see how to do these later. This is another one of the +basic Git views. + +:::::{tabs} +::::{group-tab} GitHub +In a new browser tab, open the "Insights" tab, and click on "Network". +You can hover over the commit dots to see the person who committed and +how they correspond with the commits in the other view: + :::{figure} img/browsing/network.png + :alt: Screenshot on GitHub of the network graph + :width: 100% + :class: with-border + ::: +:::: + +::::{group-tab} VS Code +We don't know how to do this without an extension. Try starting a terminal and using the +"Command line" option. +:::: + +::::{group-tab} Command line +This is a useful command to browse the network of commits locally: +```console +$ git log --graph --oneline --decorate --all +``` + +To avoid having to type this long command every time, you can define an alias (shortcut): +```console +$ git config --global alias.graph "log --graph --oneline --decorate --all" +``` + +From then on, you can use `git graph` to see the network graph. +:::: + +::::: + + +### (3) How can you browse the history of a single file? + +We see the history for the whole repository, but we can also see it +for a single file. + +:::::{tabs} + +::::{group-tab} GitHub +Navigate to the file view: Main page → simulate.py. +Click the "History" button near the top right. +:::: + +::::{group-tab} VS Code +Open simulate.py file in the editor. Under the file browser, +we see a "Timeline" view there. +:::: + +::::{group-tab} Command line +The `git log` command can take a filename and provide the log of only +a single file: + +``` +$ git log simulate.py +``` +:::: + +::::: + + +### (4) Which files include the word "position"? + +Version control makes it very easy to find all occurrences of a single +word. This is useful for things like finding where functions or +variables are defined or used. + +:::::{tabs} +::::{group-tab} GitHub +We go to the main file view. We click the Search magnifying +class at the very top, type "position", and click enter. We see every +instance, including the context. + +:::{admonition} Searching in a forked repository will not work instantaneously! + +It usually takes a few minutes before one can search for keywords in a forked repository +since it first needs to build the search index the very first time we search. +Start it, continue with other steps, then come back to this. +::: +:::: + +::::{group-tab} VS Code +If you use the "Search" magnifying class on the left sidebar, and +search for "position" it shows the occurrences in every file. You can +click to see the usage in context. +:::: + +::::{group-tab} Command line +`grep` is the command line tool that searches for lines matching a term +```console +$ git grep position # only the lines +$ git grep -C 3 position # three lines of context +$ git grep -i position # case insensitive +``` +:::: + +::::: + + +### (5) Who modified each line last and when? + +This is called the "annotate" or "blame" view. The name "blame" +is very unfortunate, but it is the standard term for historical reasons +for this functionality and it is not meant to blame anyone. + +:::::{tabs} + +::::{group-tab} GitHub +From a file view, change preview to "Blame" towards the top-left. +To get the actual commit, click on the commit message. +:::: + +::::{group-tab} VS Code +This requires an extension. We recommend for now you use the command +line version, after opening a terminal. +:::: + +::::{group-tab} Command line +These two commands are similar but have slightly different output. +```console +$ git annotate simulate.py +$ git blame simulate.py +``` +:::: + +::::: + + +### (6) Can you use this code yourself? Are you allowed to share modifications? + +- Look at the file `LICENSE`. +- On GitHub, click on the file to see a nice summary of what we can do with this: + :::{figure} img/browsing/license.png + :alt: Screenshot on GitHub summarizing license terms + :width: 100% + :class: with-border + ::: + + +### Summary + +- Git allowed us to understand this simple project much better than we + could, if it was just a few files on our own computer. +- It was easy to share the project with the course. +- By forking the repository, we created our own copy. This is + important for the following, where we will make changes to + our copy. diff --git a/_sources/version-control/motivation.md.txt b/_sources/version-control/motivation.md.txt new file mode 100644 index 0000000..25ab06f --- /dev/null +++ b/_sources/version-control/motivation.md.txt @@ -0,0 +1,118 @@ +(version-control-motivation)= + +# Motivation + +:::{objectives} +- Browse **commits** and **branches** of a Git repository. +- Remember that commits are like **snapshots** of the repository at a certain + point in time. +- Know the difference between **Git** (something that tracks changes) and + **GitHub/GitLab** (a platform to host Git repositories). +::: + + +## Why do we need to keep track of versions? + +Version control is an answer to the following questions (do you recognize some +of them?): + +- "It broke ... hopefully I have a working version somewhere?" + +- "Can you please send me the latest version?" + +- "Where is the latest version?" + +- "Which version are you using?" + +- "Which version have the authors used in the paper I am trying to reproduce?" + +- "Found a bug! Since when was it there?" + +- "I am sure it used to work. When did it change?" + +- "My laptop is gone. Is my thesis now gone?" + + +## Demonstration + +- Example repository: +- Commits are like **snapshots** and if we break something we can go back to a + previous snapshot. +- Commits carry **metadata** about changes: author, date, commit message, and + a checksum. +- **Branches** are like parallel universes where you can experiment with + changes without affecting the default branch: + + ("Insights" -> "Network") +- With version control we can **annotate code** + ([example](https://github.com/workshop-material/planets/blame/main/animate.py)). +- **Collaboration**: We can fork (make a copy on GitHub), clone (make a copy + to our computer), review, compare, share, and discuss. +- **Code review**: Others can suggest changes using pull requests or merge + requests. These can be reviewed and discussed before they are merged. + Conceptually, they are similar to "suggesting changes" in Google Docs. + + +## Features: roll-back, branching, merging, collaboration + +- **Roll-back**: you can always go back to a previous version and compare + +- **Branching and merging**: + - Work on different ideas at the same time + - Different people can work on the same code/project without interfering + - You can experiment with an idea and discard it if it turns out to be a bad idea + +:::{figure} img/gophers.png +:alt: Branching explained with a gopher +:width: 100% + +Image created using +([inspiration](https://twitter.com/jay_gee/status/703360688618536960)). +::: + +- **Collaboration**: review, compare, share, discuss + +- [Example network graph](https://github.com/workshop-material/planets/network) + + +## Talking about code + +Which of these two is more practical? +1. "Clone the code, go to the file 'simulate.py', and search for 'force_between_planets'. + Oh! But make sure you use the version from September 2024." +1. Or I can send you a permalink: + + +## What we typically like to snapshot + +- Software (this is how it started but Git/GitHub can track a lot more) +- Scripts +- Documents (plain text files much better suitable than Word documents) +- Manuscripts (Git is great for collaborating/sharing LaTeX or [Quarto](https://quarto.org/) manuscripts) +- Configuration files +- Website sources +- Data + +::::{discussion} + In this example somebody tried to keep track of versions without a version + control system tool like Git. Discuss the following directory listing. What + possible problems do you anticipate with this kind of "version control": + ```text + myproject-2019.zip + myproject-2020-february.zip + myproject-2021-august.zip + myproject-2023-09-19-working.zip + myproject-2023-09-21.zip + myproject-2023-09-21-test.zip + myproject-2023-09-21-myversion.zip + myproject-2023-09-21-newfeature.zip + ... + (100 more files like these) + ``` + + :::{solution} + - Giving a version to a collaborator and merging changes later with own + changes sounds like lots of work. + - What if you discover a bug and want to know since when the bug existed? + ::: +:::: diff --git a/_sources/version-control/practical-advice.md.txt b/_sources/version-control/practical-advice.md.txt new file mode 100644 index 0000000..5e4c460 --- /dev/null +++ b/_sources/version-control/practical-advice.md.txt @@ -0,0 +1,136 @@ +(practical-advice)= + +# Practical advice: How much Git is necessary? + + +## Writing useful commit messages + +Useful commit messages **summarize the change and provide context**. + +If you need a commit message that is longer than one line, +then the convention is: **one line summarizing the commit, then one empty line, +then paragraph(s) with more details in free form, if necessary**. + +Good example: +```{code-block} text +--- +emphasize-lines: 1 +--- +increase alpha to 2.0 for faster convergence + +the motivation for this change is +to enable ... +... +(more context) +... +this is based on a discussion in #123 +``` + +- **Why something was changed is more important than what has changed.** +- Cross-reference to issues and discussions if possible/relevant. +- Bad commit messages: "fix", "oops", "save work" +- Bad examples: [http://whatthecommit.com](http://whatthecommit.com) +- Write commit messages that will be understood + 15 years from now by someone else than you. Or by your future you. +- Many projects start out as projects "just for me" and end up to be successful projects + that are developed by 50 people over decades. +- [Commits with multiple authors](https://help.github.com/articles/creating-a-commit-with-multiple-authors/) are possible. + +Good references: + +- ["My favourite Git commit"](https://fatbusinessman.com/2019/my-favourite-git-commit) +- ["On commit messages"](https://who-t.blogspot.com/2009/12/on-commit-messages.html) +- ["How to Write a Git Commit Message"](https://chris.beams.io/posts/git-commit/) + +```{note} +A great way to learn how to write commit messages and to get inspired by their +style choices: **browse repositories of codes that you use/like**: + +Some examples (but there are so many good examples): +- [SciPy](https://github.com/scipy/scipy/commits/main) +- [NumPy](https://github.com/numpy/numpy/commits/main) +- [Pandas](https://github.com/pandas-dev/pandas/commits/main) +- [Julia](https://github.com/JuliaLang/julia/commits/master) +- [ggplot2](https://github.com/tidyverse/ggplot2/commits/main), + compare with their [release + notes](https://github.com/tidyverse/ggplot2/releases) +- [Flask](https://github.com/pallets/flask/commits/main), + compare with their [release + notes](https://github.com/pallets/flask/blob/main/CHANGES.rst) + +When designing commit message styles consider also these: +- How will you easily generate a changelog or release notes? +- During code review, you can help each other improving commit messages. +``` + +But remember: it is better to make any commit, than no commit. Especially in small projects. +**Let not the perfect be the enemy of the good enough**. + + +## What level of branching complexity is necessary for each project? + +**Simple personal projects**: +- Typically start with just the `main` branch. +- Use branches for unfinished/untested ideas. +- Use branches when you are not sure about a change. +- Use tags to mark important milestones. +- If you are unsure what to do with unfinished and not working code, commit it + to a branch. + +**Projects with few persons: you accept things breaking sometimes** +- It might be reasonable to commit to the `main` branch and feature branches. + +**Projects with few persons: changes are reviewed by others** +- You create new feature branches for changes. +- Changes are reviewed before they are merged to the `main` branch. +- Consider to write-protect the `main` branch so that it can only be changed + with pull requests or merge requests. + + +## How large should a commit be? + +- Better too small than too large (easier to combine than to split). +- Often I make a commit at the end of the day (this is a unit I would not like to lose). +- Smaller sized commits may be easier to review for others than huge commits. +- Imperfect commits are better than no commits. +- A commit should not contain unrelated changes to simplify review and possible + repair/adjustments/undo later (but again: imperfect commits are better than no commits). + + +## Working on the command line? Use "git status" all the time + +The `git status` command is one of the most useful commands in Git +to inform about which branch we are on, what we are about to commit, +which files might not be tracked, etc. + + +## How about staging and committing? + +- Commit early and often: rather create too many commits than too few. + You can always combine commits later. +- Once you commit, it is very, very hard to really lose your code. +- Always fully commit (or stash) before you do dangerous things, so that you know you are safe. + Otherwise it can be hard to recover. +- Later you can start using the staging area (where you first stage and then commit in a second step). +- Later start using `git add -p` and/or `git commit -p`. + + +## What to avoid + +- Committing **generated files/directories** (example: `__pycache__`, `*.pyc`) -> + use [.gitignore + files](https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files) + ([collection of .gitignore templates](https://github.com/github/gitignore)). + +- Committing **huge files** -> use code review to detect this. + +- Committing **unrelated changes** together. + +- Postponing commits because the changes are "unfinished"/"ugly" -> better ugly + commits than no commits. + +- When working with branches: + - Not updating your branch before starting new work. + - Too ambitious branch which risks to never get completed. + - Over-engineering the branch layout and safeguards in small projects. + - Working on unrelated things on the same branch. diff --git a/_sources/version-control/sharing.md.txt b/_sources/version-control/sharing.md.txt new file mode 100644 index 0000000..6fb6401 --- /dev/null +++ b/_sources/version-control/sharing.md.txt @@ -0,0 +1,301 @@ +(sharing-repositories)= + +# Optional: How to turn your project to a Git repo and share it + +:::{objectives} +- Turn our own coding project (small or large, finished or unfinished) into a + Git repository. +- Be able to share a repository on the web to have a backup or so that others + can reuse and collaborate or even just find it. +::: + + +## Exercise + +:::{figure} img/illustrations/sharing.png +:alt: Illustration of how we turn a project into a Git repository and share it +:width: 100% + +From a bunch of files to a local repository which we then share on GitHub. +::: + + +:::::{exercise} Exercise: Turn your project to a Git repo and share it (20 min) + +1. Create a new directory called **myproject** with one or few files in it. + This represents our own project. It is not yet a Git repository. You can try + that with your own project or use a simple placeholder example. +2. Turn this new directory into a Git repository. +3. Share this repository on GitHub (or GitLab, since it really works the same). + +We offer **three different paths** of how to do this exercise. +* Via **GitHub web interface**: easy and can be a good starting point if you are completely + new to Git. +* **VS Code** is quite easy, since VS Code can offer to create the + GitHub repositories for you. +* **Command line**: you need to create the + repository on GitHub and link it yourself. + +::::{tabs} + +:::{group-tab} Only using GitHub +### Create an repository on GitHub + +First log into GitHub, then follow the screenshots and descriptions below. +```{figure} img/sharing/new-repository.png +:alt: Screenshot on GitHub before a new repository form is opened +:width: 60% +:class: with-border + +Click on the "plus" symbol on top right, then on "New repository". +``` + +Then: +```{figure} img/sharing/create-repository-with-readme.png +:alt: Screenshot on GitHub just before a new repository is created +:width: 100% +:class: with-border + +Choose a repository name, add a short description, and in this case **make sure to check** "Add a +README file". Finally "Create repository". +``` + + +### Upload your files + +Now that the repository is created, you can upload your files: +```{figure} img/sharing/upload-files.png +:alt: Screenshot on GitHub just before uploading files +:width: 100% +:class: with-border + +Click on the "+" symbol and then on "Upload files". +``` +::: + +:::{group-tab} VS Code +In VS Code it only takes few clicks. + +First, open the folder in VS Code. My example project here contains two files. +Then click on the source control icon: +```{figure} img/sharing/vscode-start.png +:alt: Screenshot of VS Code before clicking on the source control icon +:width: 100% +:class: with-border + +Open the folder in VS Code. Then click on the source control icon. +``` + +```{figure} img/sharing/vscode-publish-to-github1.png +:alt: Screenshot of VS Code before publishing to GitHub +:width: 100% +:class: with-border + +Choose "Publish to GitHub". In this case I never even clicked on "Initialize Repository". +``` + +```{figure} img/sharing/vscode-publish-to-github2.png +:alt: Screenshot of VS Code before publishing to GitHub +:width: 100% +:class: with-border + +In my case I chose to "Publish to GitHub public repository". Here you can also rename +the repository if needed. +``` + +```{figure} img/sharing/vscode-authorize.png +:alt: Screenshot of VS Code asking for authorization +:width: 50% +:class: with-border + +First time you do this you might need to authorize VS Code to access your +GitHub account by redirecting you to https://github.com/login/oauth/authorize. +``` + +```{figure} img/sharing/vscode-publish-to-github3.png +:alt: Screenshot of VS Code after publishing to GitHub +:width: 100% +:class: with-border + +After it is published, click on "Open on GitHub". +``` +::: + +:::{group-tab} Command line +### Put your project under version control + +My example project here consists of two files. Replace this with your own +example files: +```console +$ ls -l + +.rw------- 19k user 7 Mar 17:36 LICENSE +.rw------- 21 user 7 Mar 17:36 myscript.py +``` + +I will first initialize a Git repository in this directory. +If you get an error, try without the `-b main` (and your default branch will +then be called `master`, this will happen for Git versions older than +2.28): +```console +$ git init -b main +``` + +Now add and commit the two files to the Git repository: +```console +$ git add LICENSE myscript.py +$ git commit -m "putting my project under version control" +``` + +If you want to add all files in one go, you can use `git +add .` instead of `git add LICENSE myscript.py`. + +Now you have a Git repository with one commit. Verify this with `git log`. +But it's still only on your computer. Let's put it on GitHub next. + + +### Create an empty repository on GitHub + +First log into GitHub, then follow the screenshots and descriptions below. +```{figure} img/sharing/new-repository.png +:alt: Screenshot on GitHub before a new repository form is opened +:width: 60% +:class: with-border + +Click on the "plus" symbol on top right, then on "New repository". +``` + +Then create an empty repository without any files and without any commits: +```{figure} img/sharing/create-repository.png +:alt: Screenshot on GitHub just before a new repository is created +:width: 100% +:class: with-border + +Choose a repository name, add a short description, but please **do not check** "Add a +README file". For "Add .gitignore" and "Choose a license" also leave as "None". Finally "Create repository". +``` + +Once you click the green "Create repository", you will see a page similar to: +```{figure} img/sharing/bare-repository.png +:alt: Screenshot on GitHub after a bare repository was created +:width: 100% +:class: with-border +``` + +What this means is that we have now an empty project with either an HTTPS or an +SSH address: click on the HTTPS and SSH buttons to see what happens. + + +### Push an existing repository from your computer to GitHub + +We now want to follow the "**... or push an existing repository from the command line**": + +1. In your terminal make sure you are still in your myproject directory. +2. Copy paste the three lines below the red arrow to the terminal and execute + those, in my case (**you need to replace the "USER" part and possibly also + the repository name**): + +`````{tabs} + ````{group-tab} SSH + ```console + $ git remote add origin git@github.com:USER/myproject.git + ``` + ```` + ````{group-tab} HTTPS + ```console + $ git remote add origin https://github.com/USER/myproject.git + ``` + ```` +````` + +Then: +```console +$ git branch -M main +$ git push -u origin main +``` + +The meaning of the above lines: +- Add a remote reference with the name "origin" +- Rename current branch to "main" +- Push branch "main" to "origin" + +You should now see: + +```text +Enumerating objects: 4, done. +Counting objects: 100% (4/4), done. +Delta compression using up to 12 threads +Compressing objects: 100% (3/3), done. +Writing objects: 100% (4/4), 6.08 KiB | 6.08 MiB/s, done. +Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 +To github.com:USER/myproject.git + * [new branch] main -> main +branch 'main' set up to track 'origin/main'. +``` + +Reload your GitHub project website and your commits should now be +online! + + +### Troubleshooting + +**error: remote origin already exists** +- Explanation: You probably ran a `git remote add origin ...` command, then changed your + mind about HTTPS or SSH and then tried to run the other `git remote add + origin ...` command but "origin" then already exists. +- Recovery: + - First remove "origin" with `git remote remove origin` + - Then run the correct `git remote add origin ...` command + +**remote contains work that you do not have** +- Explanation: You probably clicked on "Add a README file" and now the + repository on GitHub is not empty but contains one commit and locally you + have a different history. Git now prevents you from accidentally overwriting + the history on GitHub. +- Recovery: + - Use `git push --force` instead of `git push`, which will force Git to overwrite the history on GitHub + - Note that this is a powerful but also possibly dangerous option but here it + helps us. If it's a brand new repo, it probably is fine to do this. For real + repositories, don't do this unless you are very sure what is happening. +::: + +:::{group-tab} RStudio + +This is not fully explained, because a lot of it is similar to the "Command +line" method (and an RStudio expert could help us some). The main differences +are: + +### Put your project under version control + +1. Tools → Version control → Project setup → Version control system = Git. +1. Select "Yes" for "do you want to initialize a new git repository for this project. +1. Select yes to restart the project with RStudio. +1. Switch to branch `main` to have you branch named that. + +### Create an empty repository on GitHub + +Same as command line + +### Push an existing repository from your computer to GitHub + +1. Under the "Create new branch" button → "Add Remote" +1. Remote name: `origin` +1. Remote URL: as in command line (remember to select SSH or HTTPS as you have configured your RStudio) +1. The "Push" (up arrow) button will send changes on your current branch to the remote. The "Pull" (down arrow) will get changes from the remote. + +### Troubleshooting + +Same as command line + +::: +:::: +::::: + + +## Is putting software on GitHub/GitLab/... publishing? + +It is a good first step but to make your code truly **findable and +accessible**, consider making your code **citable and persistent**: Get a +persistent identifier (PID) such as DOI in addition to sharing the code +publicly, by using services like [Zenodo](https://zenodo.org) or similar +services. diff --git a/_static/_sphinx_javascript_frameworks_compat.js b/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000..8141580 --- /dev/null +++ b/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..f316efc --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/check-solid.svg b/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/_static/clipboard.min.js b/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/_static/copybutton.css b/_static/copybutton.css new file mode 100644 index 0000000..40eafe5 --- /dev/null +++ b/_static/copybutton.css @@ -0,0 +1,93 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +.highlight:hover button.copybtn { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/_static/copybutton.js b/_static/copybutton.js new file mode 100644 index 0000000..f3ecd03 --- /dev/null +++ b/_static/copybutton.js @@ -0,0 +1,241 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copié dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for two seconds, then changes it back +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000) + setTimeout(() => el.classList.remove('success'), 2000) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, 2000) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos, .gp'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/_static/copybutton_funcs.js b/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/_static/css/badge_only.css b/_static/css/badge_only.css new file mode 100644 index 0000000..c718cee --- /dev/null +++ b/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff b/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Bold.woff2 b/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff b/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/_static/css/fonts/Roboto-Slab-Regular.woff2 b/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/_static/css/fonts/fontawesome-webfont.eot b/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/_static/css/fonts/fontawesome-webfont.svg b/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/css/fonts/fontawesome-webfont.ttf b/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/_static/css/fonts/fontawesome-webfont.woff b/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/_static/css/fonts/fontawesome-webfont.woff2 b/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/_static/css/fonts/lato-bold-italic.woff b/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff differ diff --git a/_static/css/fonts/lato-bold-italic.woff2 b/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/_static/css/fonts/lato-bold.woff b/_static/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff differ diff --git a/_static/css/fonts/lato-bold.woff2 b/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/_static/css/fonts/lato-bold.woff2 differ diff --git a/_static/css/fonts/lato-normal-italic.woff b/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff differ diff --git a/_static/css/fonts/lato-normal-italic.woff2 b/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/_static/css/fonts/lato-normal.woff b/_static/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/_static/css/fonts/lato-normal.woff differ diff --git a/_static/css/fonts/lato-normal.woff2 b/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/_static/css/fonts/lato-normal.woff2 differ diff --git a/_static/css/theme.css b/_static/css/theme.css new file mode 100644 index 0000000..19a446a --- /dev/null +++ b/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..87fc516 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'dirhtml', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/jquery.js b/_static/jquery.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/html5shiv.min.js b/_static/js/html5shiv.min.js new file mode 100644 index 0000000..cd1c674 --- /dev/null +++ b/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/_static/js/theme.js b/_static/js/theme.js new file mode 100644 index 0000000..1fddb6e --- /dev/null +++ b/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minipres.js b/_static/minipres.js new file mode 100644 index 0000000..ad11c87 --- /dev/null +++ b/_static/minipres.js @@ -0,0 +1,223 @@ +// Add goTo method to elements +// http://stackoverflow.com/questions/4801655/how-to-go-to-a-specific-element-on-page +(function($) { + $.fn.goTo = function() { + $('html, body').animate({ + scrollTop: $(this).offset().top //+ 'px' + }, 'fast'); + return this; // for chaining... + } +})(jQuery); + +// NO good way to do this!. Copy a hack from here +// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript +// https://stackoverflow.com/a/2880929 +var urlParams; +(window.onpopstate = function () { + var match, + pl = /\+/g, // Regex for replacing addition symbol with a space + search = /([^&=]+)=?([^&]*)/g, + decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, + query = window.location.search.substring(1); + urlParams = {}; + while (match = search.exec(query)) + urlParams[decode(match[1])] = decode(match[2]); +})(); + +// Select heading levels +var maxHeading = urlParams['h'] +if (maxHeading === undefined) maxHeading = 2 +var headingLevels = []; +for (h=2 ; h (sections.length-1) ) { + // if we would scroll past bottom, or above top, do nothing + return; + } + + console.log('xxxxxx'); + var targetSection = sections[targetPos]; + console.log(targetSection, typeof(targetSection)); + + // Return targetSection top and height + var secProperties = section_top_and_height(targetSection); + var top = secProperties['top']; + var height = secProperties['height'] + var win_height = window.innerHeight; + //console.info(top, height, win_height) + + var scroll_to = 0; + if (height >= win_height || height == 0) { + scroll_to = top; + } else { + scroll_to = top - (win_height-height)/3.; + } + //console.info(top, height, win_height, scroll_to) + + $('html, body').animate({ + scrollTop: scroll_to //+ 'px' + }, 'fast'); + +} + + +function minipres() { + /* Enable the minipres mode: + - call the hide() function + - set up the scrolling listener + */ + document.addEventListener('keydown', function (event) { + switch(event.which) { + case 37: // left + switch_slide(-1); + event.preventDefault(); + return false; + break; + //case 38: // up + case 39: // right + switch_slide(+1); + event.preventDefault(); + return false; + break; + //case 40: // down + default: + return; // exit this handler for other keys + } + }, true) + + hide() + + // Increase space between sections + //$("div .section").css('margin-bottom', '50%'); + $(sectionSelector).css('margin-top', '50%'); + + // Reduce size/color of other sections + if (hiddenSectionSelector.length > 0) { + var hideNodes = $(hiddenSectionSelector); + console.log(typeof hideNodes, hideNodes); + for (node in hideNodes) { + console.log("a", typeof node, node); + node = hideNodes[node]; // what's right way to iterate values? + console.log("b", typeof node, node); + if (node.parentNode && node.parentNode.className == "section") { + node = node.parentNode; + console.log("c", typeof node, node); + //node.css['transform'] = 'scale(.5)'; + //node.css['transform-origin'] = 'top center'; + $(node).css('color', 'lightgrey'); + //$(node).css('font-size', '20%'); + //$(node).css('visibility', 'collapse'); + //ntahousnatouhasno; + } + } + } +} + +function hide() { + /* Hide all non-essential elements on the page + */ + + // This is for sphinx_rst_theme and readthedocs + $(".wy-nav-side").remove(); + $(".wy-nav-content-wrap").css('margin-left', 0); + $('.rst-versions').remove(); // readthedocs version selector + + // Add other formats here. +} + + +var slideshow = minipres; + +if (window.location.search.match(/[?&](minipres|slideshow|pres)([=&]|$)/) ) { + //minipres() + window.addEventListener("load", minipres); +} else if (window.location.search.match(/[?&](plain)([=&]|$)/) ) { + window.addEventListener("load", hide); +} diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css b/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css new file mode 100644 index 0000000..3356631 --- /dev/null +++ b/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css @@ -0,0 +1,2342 @@ +/* Variables */ +:root { + --mystnb-source-bg-color: #f7f7f7; + --mystnb-stdout-bg-color: #fcfcfc; + --mystnb-stderr-bg-color: #fdd; + --mystnb-traceback-bg-color: #fcfcfc; + --mystnb-source-border-color: #ccc; + --mystnb-source-margin-color: green; + --mystnb-stdout-border-color: #f7f7f7; + --mystnb-stderr-border-color: #f7f7f7; + --mystnb-traceback-border-color: #ffd6d6; + --mystnb-hide-prompt-opacity: 70%; + --mystnb-source-border-radius: .4em; + --mystnb-source-border-width: 1px; +} + +/* Whole cell */ +div.container.cell { + padding-left: 0; + margin-bottom: 1em; +} + +/* Removing all background formatting so we can control at the div level */ +.cell_input div.highlight, +.cell_output pre, +.cell_input pre, +.cell_output .output { + border: none; + box-shadow: none; +} + +.cell_output .output pre, +.cell_input pre { + margin: 0px; +} + +/* Input cells */ +div.cell div.cell_input, +div.cell details.above-input>summary { + padding-left: 0em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + background-color: var(--mystnb-source-bg-color); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; + border-radius: var(--mystnb-source-border-radius); +} + +div.cell_input>div, +div.cell_output div.output>div.highlight { + margin: 0em !important; + border: none !important; +} + +/* All cell outputs */ +.cell_output { + padding-left: 1em; + padding-right: 0em; + margin-top: 1em; +} + +/* Text outputs from cells */ +.cell_output .output.text_plain, +.cell_output .output.traceback, +.cell_output .output.stream, +.cell_output .output.stderr { + margin-top: 1em; + margin-bottom: 0em; + box-shadow: none; +} + +.cell_output .output.text_plain, +.cell_output .output.stream { + background: var(--mystnb-stdout-bg-color); + border: 1px solid var(--mystnb-stdout-border-color); +} + +.cell_output .output.stderr { + background: var(--mystnb-stderr-bg-color); + border: 1px solid var(--mystnb-stderr-border-color); +} + +.cell_output .output.traceback { + background: var(--mystnb-traceback-bg-color); + border: 1px solid var(--mystnb-traceback-border-color); +} + +/* Collapsible cell content */ +div.cell details.above-input div.cell_input { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; +} + +div.cell div.cell_input.above-output-prompt { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +div.cell details.above-input>summary { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; + padding-left: 1em; + margin-bottom: 0; +} + +div.cell details.above-output>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.below-input>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-top: none; + border-bottom-left-radius: var(--mystnb-source-border-radius); + border-bottom-right-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.hide>summary>span { + opacity: var(--mystnb-hide-prompt-opacity); +} + +div.cell details.hide[open]>summary>span.collapsed { + display: none; +} + +div.cell details.hide:not([open])>summary>span.expanded { + display: none; +} + +@keyframes collapsed-fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} +div.cell details.hide[open]>summary~* { + -moz-animation: collapsed-fade-in 0.3s ease-in-out; + -webkit-animation: collapsed-fade-in 0.3s ease-in-out; + animation: collapsed-fade-in 0.3s ease-in-out; +} + +/* Math align to the left */ +.cell_output .MathJax_Display { + text-align: left !important; +} + +/* Pandas tables. Pulled from the Jupyter / nbsphinx CSS */ +div.cell_output table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 1em; + table-layout: fixed; +} + +div.cell_output thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} + +div.cell_output tr, +div.cell_output th, +div.cell_output td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} + +div.cell_output th { + font-weight: bold; +} + +div.cell_output tbody tr:nth-child(odd) { + background: #f5f5f5; +} + +div.cell_output tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + +/** source code line numbers **/ +span.linenos { + opacity: 0.5; +} + +/* Inline text from `paste` operation */ + +span.pasted-text { + font-weight: bold; +} + +span.pasted-inline img { + max-height: 2em; +} + +tbody span.pasted-inline img { + max-height: none; +} + +/* Font colors for translated ANSI escape sequences +Color values are copied from Jupyter Notebook +https://github.com/jupyter/notebook/blob/52581f8eda9b319eb0390ac77fe5903c38f81e3e/notebook/static/notebook/less/ansicolors.less#L14-L21 +Background colors from +https://nbsphinx.readthedocs.io/en/latest/code-cells.html#ANSI-Colors +*/ +div.highlight .-Color-Bold { + font-weight: bold; +} + +div.highlight .-Color[class*=-Black] { + color: #3E424D +} + +div.highlight .-Color[class*=-Red] { + color: #E75C58 +} + +div.highlight .-Color[class*=-Green] { + color: #00A250 +} + +div.highlight .-Color[class*=-Yellow] { + color: #DDB62B +} + +div.highlight .-Color[class*=-Blue] { + color: #208FFB +} + +div.highlight .-Color[class*=-Magenta] { + color: #D160C4 +} + +div.highlight .-Color[class*=-Cyan] { + color: #60C6C8 +} + +div.highlight .-Color[class*=-White] { + color: #C5C1B4 +} + +div.highlight .-Color[class*=-BGBlack] { + background-color: #3E424D +} + +div.highlight .-Color[class*=-BGRed] { + background-color: #E75C58 +} + +div.highlight .-Color[class*=-BGGreen] { + background-color: #00A250 +} + +div.highlight .-Color[class*=-BGYellow] { + background-color: #DDB62B +} + +div.highlight .-Color[class*=-BGBlue] { + background-color: #208FFB +} + +div.highlight .-Color[class*=-BGMagenta] { + background-color: #D160C4 +} + +div.highlight .-Color[class*=-BGCyan] { + background-color: #60C6C8 +} + +div.highlight .-Color[class*=-BGWhite] { + background-color: #C5C1B4 +} + +/* Font colors for 8-bit ANSI */ + +div.highlight .-Color[class*=-C0] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC0] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C1] { + color: #800000 +} + +div.highlight .-Color[class*=-BGC1] { + background-color: #800000 +} + +div.highlight .-Color[class*=-C2] { + color: #008000 +} + +div.highlight .-Color[class*=-BGC2] { + background-color: #008000 +} + +div.highlight .-Color[class*=-C3] { + color: #808000 +} + +div.highlight .-Color[class*=-BGC3] { + background-color: #808000 +} + +div.highlight .-Color[class*=-C4] { + color: #000080 +} + +div.highlight .-Color[class*=-BGC4] { + background-color: #000080 +} + +div.highlight .-Color[class*=-C5] { + color: #800080 +} + +div.highlight .-Color[class*=-BGC5] { + background-color: #800080 +} + +div.highlight .-Color[class*=-C6] { + color: #008080 +} + +div.highlight .-Color[class*=-BGC6] { + background-color: #008080 +} + +div.highlight .-Color[class*=-C7] { + color: #C0C0C0 +} + +div.highlight .-Color[class*=-BGC7] { + background-color: #C0C0C0 +} + +div.highlight .-Color[class*=-C8] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC8] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C9] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC9] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C10] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC10] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C11] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC11] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C12] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC12] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C13] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC13] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C14] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC14] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C15] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC15] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C16] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC16] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C17] { + color: #00005F +} + +div.highlight .-Color[class*=-BGC17] { + background-color: #00005F +} + +div.highlight .-Color[class*=-C18] { + color: #000087 +} + +div.highlight .-Color[class*=-BGC18] { + background-color: #000087 +} + +div.highlight .-Color[class*=-C19] { + color: #0000AF +} + +div.highlight .-Color[class*=-BGC19] { + background-color: #0000AF +} + +div.highlight .-Color[class*=-C20] { + color: #0000D7 +} + +div.highlight .-Color[class*=-BGC20] { + background-color: #0000D7 +} + +div.highlight .-Color[class*=-C21] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC21] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C22] { + color: #005F00 +} + +div.highlight .-Color[class*=-BGC22] { + background-color: #005F00 +} + +div.highlight .-Color[class*=-C23] { + color: #005F5F +} + +div.highlight .-Color[class*=-BGC23] { + background-color: #005F5F +} + +div.highlight .-Color[class*=-C24] { + color: #005F87 +} + +div.highlight .-Color[class*=-BGC24] { + background-color: #005F87 +} + +div.highlight .-Color[class*=-C25] { + color: #005FAF +} + +div.highlight .-Color[class*=-BGC25] { + background-color: #005FAF +} + +div.highlight .-Color[class*=-C26] { + color: #005FD7 +} + +div.highlight .-Color[class*=-BGC26] { + background-color: #005FD7 +} + +div.highlight .-Color[class*=-C27] { + color: #005FFF +} + +div.highlight .-Color[class*=-BGC27] { + background-color: #005FFF +} + +div.highlight .-Color[class*=-C28] { + color: #008700 +} + +div.highlight .-Color[class*=-BGC28] { + background-color: #008700 +} + +div.highlight .-Color[class*=-C29] { + color: #00875F +} + +div.highlight .-Color[class*=-BGC29] { + background-color: #00875F +} + +div.highlight .-Color[class*=-C30] { + color: #008787 +} + +div.highlight .-Color[class*=-BGC30] { + background-color: #008787 +} + +div.highlight .-Color[class*=-C31] { + color: #0087AF +} + +div.highlight .-Color[class*=-BGC31] { + background-color: #0087AF +} + +div.highlight .-Color[class*=-C32] { + color: #0087D7 +} + +div.highlight .-Color[class*=-BGC32] { + background-color: #0087D7 +} + +div.highlight .-Color[class*=-C33] { + color: #0087FF +} + +div.highlight .-Color[class*=-BGC33] { + background-color: #0087FF +} + +div.highlight .-Color[class*=-C34] { + color: #00AF00 +} + +div.highlight .-Color[class*=-BGC34] { + background-color: #00AF00 +} + +div.highlight .-Color[class*=-C35] { + color: #00AF5F +} + +div.highlight .-Color[class*=-BGC35] { + background-color: #00AF5F +} + +div.highlight .-Color[class*=-C36] { + color: #00AF87 +} + +div.highlight .-Color[class*=-BGC36] { + background-color: #00AF87 +} + +div.highlight .-Color[class*=-C37] { + color: #00AFAF +} + +div.highlight .-Color[class*=-BGC37] { + background-color: #00AFAF +} + +div.highlight .-Color[class*=-C38] { + color: #00AFD7 +} + +div.highlight .-Color[class*=-BGC38] { + background-color: #00AFD7 +} + +div.highlight .-Color[class*=-C39] { + color: #00AFFF +} + +div.highlight .-Color[class*=-BGC39] { + background-color: #00AFFF +} + +div.highlight .-Color[class*=-C40] { + color: #00D700 +} + +div.highlight .-Color[class*=-BGC40] { + background-color: #00D700 +} + +div.highlight .-Color[class*=-C41] { + color: #00D75F +} + +div.highlight .-Color[class*=-BGC41] { + background-color: #00D75F +} + +div.highlight .-Color[class*=-C42] { + color: #00D787 +} + +div.highlight .-Color[class*=-BGC42] { + background-color: #00D787 +} + +div.highlight .-Color[class*=-C43] { + color: #00D7AF +} + +div.highlight .-Color[class*=-BGC43] { + background-color: #00D7AF +} + +div.highlight .-Color[class*=-C44] { + color: #00D7D7 +} + +div.highlight .-Color[class*=-BGC44] { + background-color: #00D7D7 +} + +div.highlight .-Color[class*=-C45] { + color: #00D7FF +} + +div.highlight .-Color[class*=-BGC45] { + background-color: #00D7FF +} + +div.highlight .-Color[class*=-C46] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC46] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C47] { + color: #00FF5F +} + +div.highlight .-Color[class*=-BGC47] { + background-color: #00FF5F +} + +div.highlight .-Color[class*=-C48] { + color: #00FF87 +} + +div.highlight .-Color[class*=-BGC48] { + background-color: #00FF87 +} + +div.highlight .-Color[class*=-C49] { + color: #00FFAF +} + +div.highlight .-Color[class*=-BGC49] { + background-color: #00FFAF +} + +div.highlight .-Color[class*=-C50] { + color: #00FFD7 +} + +div.highlight .-Color[class*=-BGC50] { + background-color: #00FFD7 +} + +div.highlight .-Color[class*=-C51] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC51] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C52] { + color: #5F0000 +} + +div.highlight .-Color[class*=-BGC52] { + background-color: #5F0000 +} + +div.highlight .-Color[class*=-C53] { + color: #5F005F +} + +div.highlight .-Color[class*=-BGC53] { + background-color: #5F005F +} + +div.highlight .-Color[class*=-C54] { + color: #5F0087 +} + +div.highlight .-Color[class*=-BGC54] { + background-color: #5F0087 +} + +div.highlight .-Color[class*=-C55] { + color: #5F00AF +} + +div.highlight .-Color[class*=-BGC55] { + background-color: #5F00AF +} + +div.highlight .-Color[class*=-C56] { + color: #5F00D7 +} + +div.highlight .-Color[class*=-BGC56] { + background-color: #5F00D7 +} + +div.highlight .-Color[class*=-C57] { + color: #5F00FF +} + +div.highlight .-Color[class*=-BGC57] { + background-color: #5F00FF +} + +div.highlight .-Color[class*=-C58] { + color: #5F5F00 +} + +div.highlight .-Color[class*=-BGC58] { + background-color: #5F5F00 +} + +div.highlight .-Color[class*=-C59] { + color: #5F5F5F +} + +div.highlight .-Color[class*=-BGC59] { + background-color: #5F5F5F +} + +div.highlight .-Color[class*=-C60] { + color: #5F5F87 +} + +div.highlight .-Color[class*=-BGC60] { + background-color: #5F5F87 +} + +div.highlight .-Color[class*=-C61] { + color: #5F5FAF +} + +div.highlight .-Color[class*=-BGC61] { + background-color: #5F5FAF +} + +div.highlight .-Color[class*=-C62] { + color: #5F5FD7 +} + +div.highlight .-Color[class*=-BGC62] { + background-color: #5F5FD7 +} + +div.highlight .-Color[class*=-C63] { + color: #5F5FFF +} + +div.highlight .-Color[class*=-BGC63] { + background-color: #5F5FFF +} + +div.highlight .-Color[class*=-C64] { + color: #5F8700 +} + +div.highlight .-Color[class*=-BGC64] { + background-color: #5F8700 +} + +div.highlight .-Color[class*=-C65] { + color: #5F875F +} + +div.highlight .-Color[class*=-BGC65] { + background-color: #5F875F +} + +div.highlight .-Color[class*=-C66] { + color: #5F8787 +} + +div.highlight .-Color[class*=-BGC66] { + background-color: #5F8787 +} + +div.highlight .-Color[class*=-C67] { + color: #5F87AF +} + +div.highlight .-Color[class*=-BGC67] { + background-color: #5F87AF +} + +div.highlight .-Color[class*=-C68] { + color: #5F87D7 +} + +div.highlight .-Color[class*=-BGC68] { + background-color: #5F87D7 +} + +div.highlight .-Color[class*=-C69] { + color: #5F87FF +} + +div.highlight .-Color[class*=-BGC69] { + background-color: #5F87FF +} + +div.highlight .-Color[class*=-C70] { + color: #5FAF00 +} + +div.highlight .-Color[class*=-BGC70] { + background-color: #5FAF00 +} + +div.highlight .-Color[class*=-C71] { + color: #5FAF5F +} + +div.highlight .-Color[class*=-BGC71] { + background-color: #5FAF5F +} + +div.highlight .-Color[class*=-C72] { + color: #5FAF87 +} + +div.highlight .-Color[class*=-BGC72] { + background-color: #5FAF87 +} + +div.highlight .-Color[class*=-C73] { + color: #5FAFAF +} + +div.highlight .-Color[class*=-BGC73] { + background-color: #5FAFAF +} + +div.highlight .-Color[class*=-C74] { + color: #5FAFD7 +} + +div.highlight .-Color[class*=-BGC74] { + background-color: #5FAFD7 +} + +div.highlight .-Color[class*=-C75] { + color: #5FAFFF +} + +div.highlight .-Color[class*=-BGC75] { + background-color: #5FAFFF +} + +div.highlight .-Color[class*=-C76] { + color: #5FD700 +} + +div.highlight .-Color[class*=-BGC76] { + background-color: #5FD700 +} + +div.highlight .-Color[class*=-C77] { + color: #5FD75F +} + +div.highlight .-Color[class*=-BGC77] { + background-color: #5FD75F +} + +div.highlight .-Color[class*=-C78] { + color: #5FD787 +} + +div.highlight .-Color[class*=-BGC78] { + background-color: #5FD787 +} + +div.highlight .-Color[class*=-C79] { + color: #5FD7AF +} + +div.highlight .-Color[class*=-BGC79] { + background-color: #5FD7AF +} + +div.highlight .-Color[class*=-C80] { + color: #5FD7D7 +} + +div.highlight .-Color[class*=-BGC80] { + background-color: #5FD7D7 +} + +div.highlight .-Color[class*=-C81] { + color: #5FD7FF +} + +div.highlight .-Color[class*=-BGC81] { + background-color: #5FD7FF +} + +div.highlight .-Color[class*=-C82] { + color: #5FFF00 +} + +div.highlight .-Color[class*=-BGC82] { + background-color: #5FFF00 +} + +div.highlight .-Color[class*=-C83] { + color: #5FFF5F +} + +div.highlight .-Color[class*=-BGC83] { + background-color: #5FFF5F +} + +div.highlight .-Color[class*=-C84] { + color: #5FFF87 +} + +div.highlight .-Color[class*=-BGC84] { + background-color: #5FFF87 +} + +div.highlight .-Color[class*=-C85] { + color: #5FFFAF +} + +div.highlight .-Color[class*=-BGC85] { + background-color: #5FFFAF +} + +div.highlight .-Color[class*=-C86] { + color: #5FFFD7 +} + +div.highlight .-Color[class*=-BGC86] { + background-color: #5FFFD7 +} + +div.highlight .-Color[class*=-C87] { + color: #5FFFFF +} + +div.highlight .-Color[class*=-BGC87] { + background-color: #5FFFFF +} + +div.highlight .-Color[class*=-C88] { + color: #870000 +} + +div.highlight .-Color[class*=-BGC88] { + background-color: #870000 +} + +div.highlight .-Color[class*=-C89] { + color: #87005F +} + +div.highlight .-Color[class*=-BGC89] { + background-color: #87005F +} + +div.highlight .-Color[class*=-C90] { + color: #870087 +} + +div.highlight .-Color[class*=-BGC90] { + background-color: #870087 +} + +div.highlight .-Color[class*=-C91] { + color: #8700AF +} + +div.highlight .-Color[class*=-BGC91] { + background-color: #8700AF +} + +div.highlight .-Color[class*=-C92] { + color: #8700D7 +} + +div.highlight .-Color[class*=-BGC92] { + background-color: #8700D7 +} + +div.highlight .-Color[class*=-C93] { + color: #8700FF +} + +div.highlight .-Color[class*=-BGC93] { + background-color: #8700FF +} + +div.highlight .-Color[class*=-C94] { + color: #875F00 +} + +div.highlight .-Color[class*=-BGC94] { + background-color: #875F00 +} + +div.highlight .-Color[class*=-C95] { + color: #875F5F +} + +div.highlight .-Color[class*=-BGC95] { + background-color: #875F5F +} + +div.highlight .-Color[class*=-C96] { + color: #875F87 +} + +div.highlight .-Color[class*=-BGC96] { + background-color: #875F87 +} + +div.highlight .-Color[class*=-C97] { + color: #875FAF +} + +div.highlight .-Color[class*=-BGC97] { + background-color: #875FAF +} + +div.highlight .-Color[class*=-C98] { + color: #875FD7 +} + +div.highlight .-Color[class*=-BGC98] { + background-color: #875FD7 +} + +div.highlight .-Color[class*=-C99] { + color: #875FFF +} + +div.highlight .-Color[class*=-BGC99] { + background-color: #875FFF +} + +div.highlight .-Color[class*=-C100] { + color: #878700 +} + +div.highlight .-Color[class*=-BGC100] { + background-color: #878700 +} + +div.highlight .-Color[class*=-C101] { + color: #87875F +} + +div.highlight .-Color[class*=-BGC101] { + background-color: #87875F +} + +div.highlight .-Color[class*=-C102] { + color: #878787 +} + +div.highlight .-Color[class*=-BGC102] { + background-color: #878787 +} + +div.highlight .-Color[class*=-C103] { + color: #8787AF +} + +div.highlight .-Color[class*=-BGC103] { + background-color: #8787AF +} + +div.highlight .-Color[class*=-C104] { + color: #8787D7 +} + +div.highlight .-Color[class*=-BGC104] { + background-color: #8787D7 +} + +div.highlight .-Color[class*=-C105] { + color: #8787FF +} + +div.highlight .-Color[class*=-BGC105] { + background-color: #8787FF +} + +div.highlight .-Color[class*=-C106] { + color: #87AF00 +} + +div.highlight .-Color[class*=-BGC106] { + background-color: #87AF00 +} + +div.highlight .-Color[class*=-C107] { + color: #87AF5F +} + +div.highlight .-Color[class*=-BGC107] { + background-color: #87AF5F +} + +div.highlight .-Color[class*=-C108] { + color: #87AF87 +} + +div.highlight .-Color[class*=-BGC108] { + background-color: #87AF87 +} + +div.highlight .-Color[class*=-C109] { + color: #87AFAF +} + +div.highlight .-Color[class*=-BGC109] { + background-color: #87AFAF +} + +div.highlight .-Color[class*=-C110] { + color: #87AFD7 +} + +div.highlight .-Color[class*=-BGC110] { + background-color: #87AFD7 +} + +div.highlight .-Color[class*=-C111] { + color: #87AFFF +} + +div.highlight .-Color[class*=-BGC111] { + background-color: #87AFFF +} + +div.highlight .-Color[class*=-C112] { + color: #87D700 +} + +div.highlight .-Color[class*=-BGC112] { + background-color: #87D700 +} + +div.highlight .-Color[class*=-C113] { + color: #87D75F +} + +div.highlight .-Color[class*=-BGC113] { + background-color: #87D75F +} + +div.highlight .-Color[class*=-C114] { + color: #87D787 +} + +div.highlight .-Color[class*=-BGC114] { + background-color: #87D787 +} + +div.highlight .-Color[class*=-C115] { + color: #87D7AF +} + +div.highlight .-Color[class*=-BGC115] { + background-color: #87D7AF +} + +div.highlight .-Color[class*=-C116] { + color: #87D7D7 +} + +div.highlight .-Color[class*=-BGC116] { + background-color: #87D7D7 +} + +div.highlight .-Color[class*=-C117] { + color: #87D7FF +} + +div.highlight .-Color[class*=-BGC117] { + background-color: #87D7FF +} + +div.highlight .-Color[class*=-C118] { + color: #87FF00 +} + +div.highlight .-Color[class*=-BGC118] { + background-color: #87FF00 +} + +div.highlight .-Color[class*=-C119] { + color: #87FF5F +} + +div.highlight .-Color[class*=-BGC119] { + background-color: #87FF5F +} + +div.highlight .-Color[class*=-C120] { + color: #87FF87 +} + +div.highlight .-Color[class*=-BGC120] { + background-color: #87FF87 +} + +div.highlight .-Color[class*=-C121] { + color: #87FFAF +} + +div.highlight .-Color[class*=-BGC121] { + background-color: #87FFAF +} + +div.highlight .-Color[class*=-C122] { + color: #87FFD7 +} + +div.highlight .-Color[class*=-BGC122] { + background-color: #87FFD7 +} + +div.highlight .-Color[class*=-C123] { + color: #87FFFF +} + +div.highlight .-Color[class*=-BGC123] { + background-color: #87FFFF +} + +div.highlight .-Color[class*=-C124] { + color: #AF0000 +} + +div.highlight .-Color[class*=-BGC124] { + background-color: #AF0000 +} + +div.highlight .-Color[class*=-C125] { + color: #AF005F +} + +div.highlight .-Color[class*=-BGC125] { + background-color: #AF005F +} + +div.highlight .-Color[class*=-C126] { + color: #AF0087 +} + +div.highlight .-Color[class*=-BGC126] { + background-color: #AF0087 +} + +div.highlight .-Color[class*=-C127] { + color: #AF00AF +} + +div.highlight .-Color[class*=-BGC127] { + background-color: #AF00AF +} + +div.highlight .-Color[class*=-C128] { + color: #AF00D7 +} + +div.highlight .-Color[class*=-BGC128] { + background-color: #AF00D7 +} + +div.highlight .-Color[class*=-C129] { + color: #AF00FF +} + +div.highlight .-Color[class*=-BGC129] { + background-color: #AF00FF +} + +div.highlight .-Color[class*=-C130] { + color: #AF5F00 +} + +div.highlight .-Color[class*=-BGC130] { + background-color: #AF5F00 +} + +div.highlight .-Color[class*=-C131] { + color: #AF5F5F +} + +div.highlight .-Color[class*=-BGC131] { + background-color: #AF5F5F +} + +div.highlight .-Color[class*=-C132] { + color: #AF5F87 +} + +div.highlight .-Color[class*=-BGC132] { + background-color: #AF5F87 +} + +div.highlight .-Color[class*=-C133] { + color: #AF5FAF +} + +div.highlight .-Color[class*=-BGC133] { + background-color: #AF5FAF +} + +div.highlight .-Color[class*=-C134] { + color: #AF5FD7 +} + +div.highlight .-Color[class*=-BGC134] { + background-color: #AF5FD7 +} + +div.highlight .-Color[class*=-C135] { + color: #AF5FFF +} + +div.highlight .-Color[class*=-BGC135] { + background-color: #AF5FFF +} + +div.highlight .-Color[class*=-C136] { + color: #AF8700 +} + +div.highlight .-Color[class*=-BGC136] { + background-color: #AF8700 +} + +div.highlight .-Color[class*=-C137] { + color: #AF875F +} + +div.highlight .-Color[class*=-BGC137] { + background-color: #AF875F +} + +div.highlight .-Color[class*=-C138] { + color: #AF8787 +} + +div.highlight .-Color[class*=-BGC138] { + background-color: #AF8787 +} + +div.highlight .-Color[class*=-C139] { + color: #AF87AF +} + +div.highlight .-Color[class*=-BGC139] { + background-color: #AF87AF +} + +div.highlight .-Color[class*=-C140] { + color: #AF87D7 +} + +div.highlight .-Color[class*=-BGC140] { + background-color: #AF87D7 +} + +div.highlight .-Color[class*=-C141] { + color: #AF87FF +} + +div.highlight .-Color[class*=-BGC141] { + background-color: #AF87FF +} + +div.highlight .-Color[class*=-C142] { + color: #AFAF00 +} + +div.highlight .-Color[class*=-BGC142] { + background-color: #AFAF00 +} + +div.highlight .-Color[class*=-C143] { + color: #AFAF5F +} + +div.highlight .-Color[class*=-BGC143] { + background-color: #AFAF5F +} + +div.highlight .-Color[class*=-C144] { + color: #AFAF87 +} + +div.highlight .-Color[class*=-BGC144] { + background-color: #AFAF87 +} + +div.highlight .-Color[class*=-C145] { + color: #AFAFAF +} + +div.highlight .-Color[class*=-BGC145] { + background-color: #AFAFAF +} + +div.highlight .-Color[class*=-C146] { + color: #AFAFD7 +} + +div.highlight .-Color[class*=-BGC146] { + background-color: #AFAFD7 +} + +div.highlight .-Color[class*=-C147] { + color: #AFAFFF +} + +div.highlight .-Color[class*=-BGC147] { + background-color: #AFAFFF +} + +div.highlight .-Color[class*=-C148] { + color: #AFD700 +} + +div.highlight .-Color[class*=-BGC148] { + background-color: #AFD700 +} + +div.highlight .-Color[class*=-C149] { + color: #AFD75F +} + +div.highlight .-Color[class*=-BGC149] { + background-color: #AFD75F +} + +div.highlight .-Color[class*=-C150] { + color: #AFD787 +} + +div.highlight .-Color[class*=-BGC150] { + background-color: #AFD787 +} + +div.highlight .-Color[class*=-C151] { + color: #AFD7AF +} + +div.highlight .-Color[class*=-BGC151] { + background-color: #AFD7AF +} + +div.highlight .-Color[class*=-C152] { + color: #AFD7D7 +} + +div.highlight .-Color[class*=-BGC152] { + background-color: #AFD7D7 +} + +div.highlight .-Color[class*=-C153] { + color: #AFD7FF +} + +div.highlight .-Color[class*=-BGC153] { + background-color: #AFD7FF +} + +div.highlight .-Color[class*=-C154] { + color: #AFFF00 +} + +div.highlight .-Color[class*=-BGC154] { + background-color: #AFFF00 +} + +div.highlight .-Color[class*=-C155] { + color: #AFFF5F +} + +div.highlight .-Color[class*=-BGC155] { + background-color: #AFFF5F +} + +div.highlight .-Color[class*=-C156] { + color: #AFFF87 +} + +div.highlight .-Color[class*=-BGC156] { + background-color: #AFFF87 +} + +div.highlight .-Color[class*=-C157] { + color: #AFFFAF +} + +div.highlight .-Color[class*=-BGC157] { + background-color: #AFFFAF +} + +div.highlight .-Color[class*=-C158] { + color: #AFFFD7 +} + +div.highlight .-Color[class*=-BGC158] { + background-color: #AFFFD7 +} + +div.highlight .-Color[class*=-C159] { + color: #AFFFFF +} + +div.highlight .-Color[class*=-BGC159] { + background-color: #AFFFFF +} + +div.highlight .-Color[class*=-C160] { + color: #D70000 +} + +div.highlight .-Color[class*=-BGC160] { + background-color: #D70000 +} + +div.highlight .-Color[class*=-C161] { + color: #D7005F +} + +div.highlight .-Color[class*=-BGC161] { + background-color: #D7005F +} + +div.highlight .-Color[class*=-C162] { + color: #D70087 +} + +div.highlight .-Color[class*=-BGC162] { + background-color: #D70087 +} + +div.highlight .-Color[class*=-C163] { + color: #D700AF +} + +div.highlight .-Color[class*=-BGC163] { + background-color: #D700AF +} + +div.highlight .-Color[class*=-C164] { + color: #D700D7 +} + +div.highlight .-Color[class*=-BGC164] { + background-color: #D700D7 +} + +div.highlight .-Color[class*=-C165] { + color: #D700FF +} + +div.highlight .-Color[class*=-BGC165] { + background-color: #D700FF +} + +div.highlight .-Color[class*=-C166] { + color: #D75F00 +} + +div.highlight .-Color[class*=-BGC166] { + background-color: #D75F00 +} + +div.highlight .-Color[class*=-C167] { + color: #D75F5F +} + +div.highlight .-Color[class*=-BGC167] { + background-color: #D75F5F +} + +div.highlight .-Color[class*=-C168] { + color: #D75F87 +} + +div.highlight .-Color[class*=-BGC168] { + background-color: #D75F87 +} + +div.highlight .-Color[class*=-C169] { + color: #D75FAF +} + +div.highlight .-Color[class*=-BGC169] { + background-color: #D75FAF +} + +div.highlight .-Color[class*=-C170] { + color: #D75FD7 +} + +div.highlight .-Color[class*=-BGC170] { + background-color: #D75FD7 +} + +div.highlight .-Color[class*=-C171] { + color: #D75FFF +} + +div.highlight .-Color[class*=-BGC171] { + background-color: #D75FFF +} + +div.highlight .-Color[class*=-C172] { + color: #D78700 +} + +div.highlight .-Color[class*=-BGC172] { + background-color: #D78700 +} + +div.highlight .-Color[class*=-C173] { + color: #D7875F +} + +div.highlight .-Color[class*=-BGC173] { + background-color: #D7875F +} + +div.highlight .-Color[class*=-C174] { + color: #D78787 +} + +div.highlight .-Color[class*=-BGC174] { + background-color: #D78787 +} + +div.highlight .-Color[class*=-C175] { + color: #D787AF +} + +div.highlight .-Color[class*=-BGC175] { + background-color: #D787AF +} + +div.highlight .-Color[class*=-C176] { + color: #D787D7 +} + +div.highlight .-Color[class*=-BGC176] { + background-color: #D787D7 +} + +div.highlight .-Color[class*=-C177] { + color: #D787FF +} + +div.highlight .-Color[class*=-BGC177] { + background-color: #D787FF +} + +div.highlight .-Color[class*=-C178] { + color: #D7AF00 +} + +div.highlight .-Color[class*=-BGC178] { + background-color: #D7AF00 +} + +div.highlight .-Color[class*=-C179] { + color: #D7AF5F +} + +div.highlight .-Color[class*=-BGC179] { + background-color: #D7AF5F +} + +div.highlight .-Color[class*=-C180] { + color: #D7AF87 +} + +div.highlight .-Color[class*=-BGC180] { + background-color: #D7AF87 +} + +div.highlight .-Color[class*=-C181] { + color: #D7AFAF +} + +div.highlight .-Color[class*=-BGC181] { + background-color: #D7AFAF +} + +div.highlight .-Color[class*=-C182] { + color: #D7AFD7 +} + +div.highlight .-Color[class*=-BGC182] { + background-color: #D7AFD7 +} + +div.highlight .-Color[class*=-C183] { + color: #D7AFFF +} + +div.highlight .-Color[class*=-BGC183] { + background-color: #D7AFFF +} + +div.highlight .-Color[class*=-C184] { + color: #D7D700 +} + +div.highlight .-Color[class*=-BGC184] { + background-color: #D7D700 +} + +div.highlight .-Color[class*=-C185] { + color: #D7D75F +} + +div.highlight .-Color[class*=-BGC185] { + background-color: #D7D75F +} + +div.highlight .-Color[class*=-C186] { + color: #D7D787 +} + +div.highlight .-Color[class*=-BGC186] { + background-color: #D7D787 +} + +div.highlight .-Color[class*=-C187] { + color: #D7D7AF +} + +div.highlight .-Color[class*=-BGC187] { + background-color: #D7D7AF +} + +div.highlight .-Color[class*=-C188] { + color: #D7D7D7 +} + +div.highlight .-Color[class*=-BGC188] { + background-color: #D7D7D7 +} + +div.highlight .-Color[class*=-C189] { + color: #D7D7FF +} + +div.highlight .-Color[class*=-BGC189] { + background-color: #D7D7FF +} + +div.highlight .-Color[class*=-C190] { + color: #D7FF00 +} + +div.highlight .-Color[class*=-BGC190] { + background-color: #D7FF00 +} + +div.highlight .-Color[class*=-C191] { + color: #D7FF5F +} + +div.highlight .-Color[class*=-BGC191] { + background-color: #D7FF5F +} + +div.highlight .-Color[class*=-C192] { + color: #D7FF87 +} + +div.highlight .-Color[class*=-BGC192] { + background-color: #D7FF87 +} + +div.highlight .-Color[class*=-C193] { + color: #D7FFAF +} + +div.highlight .-Color[class*=-BGC193] { + background-color: #D7FFAF +} + +div.highlight .-Color[class*=-C194] { + color: #D7FFD7 +} + +div.highlight .-Color[class*=-BGC194] { + background-color: #D7FFD7 +} + +div.highlight .-Color[class*=-C195] { + color: #D7FFFF +} + +div.highlight .-Color[class*=-BGC195] { + background-color: #D7FFFF +} + +div.highlight .-Color[class*=-C196] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC196] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C197] { + color: #FF005F +} + +div.highlight .-Color[class*=-BGC197] { + background-color: #FF005F +} + +div.highlight .-Color[class*=-C198] { + color: #FF0087 +} + +div.highlight .-Color[class*=-BGC198] { + background-color: #FF0087 +} + +div.highlight .-Color[class*=-C199] { + color: #FF00AF +} + +div.highlight .-Color[class*=-BGC199] { + background-color: #FF00AF +} + +div.highlight .-Color[class*=-C200] { + color: #FF00D7 +} + +div.highlight .-Color[class*=-BGC200] { + background-color: #FF00D7 +} + +div.highlight .-Color[class*=-C201] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC201] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C202] { + color: #FF5F00 +} + +div.highlight .-Color[class*=-BGC202] { + background-color: #FF5F00 +} + +div.highlight .-Color[class*=-C203] { + color: #FF5F5F +} + +div.highlight .-Color[class*=-BGC203] { + background-color: #FF5F5F +} + +div.highlight .-Color[class*=-C204] { + color: #FF5F87 +} + +div.highlight .-Color[class*=-BGC204] { + background-color: #FF5F87 +} + +div.highlight .-Color[class*=-C205] { + color: #FF5FAF +} + +div.highlight .-Color[class*=-BGC205] { + background-color: #FF5FAF +} + +div.highlight .-Color[class*=-C206] { + color: #FF5FD7 +} + +div.highlight .-Color[class*=-BGC206] { + background-color: #FF5FD7 +} + +div.highlight .-Color[class*=-C207] { + color: #FF5FFF +} + +div.highlight .-Color[class*=-BGC207] { + background-color: #FF5FFF +} + +div.highlight .-Color[class*=-C208] { + color: #FF8700 +} + +div.highlight .-Color[class*=-BGC208] { + background-color: #FF8700 +} + +div.highlight .-Color[class*=-C209] { + color: #FF875F +} + +div.highlight .-Color[class*=-BGC209] { + background-color: #FF875F +} + +div.highlight .-Color[class*=-C210] { + color: #FF8787 +} + +div.highlight .-Color[class*=-BGC210] { + background-color: #FF8787 +} + +div.highlight .-Color[class*=-C211] { + color: #FF87AF +} + +div.highlight .-Color[class*=-BGC211] { + background-color: #FF87AF +} + +div.highlight .-Color[class*=-C212] { + color: #FF87D7 +} + +div.highlight .-Color[class*=-BGC212] { + background-color: #FF87D7 +} + +div.highlight .-Color[class*=-C213] { + color: #FF87FF +} + +div.highlight .-Color[class*=-BGC213] { + background-color: #FF87FF +} + +div.highlight .-Color[class*=-C214] { + color: #FFAF00 +} + +div.highlight .-Color[class*=-BGC214] { + background-color: #FFAF00 +} + +div.highlight .-Color[class*=-C215] { + color: #FFAF5F +} + +div.highlight .-Color[class*=-BGC215] { + background-color: #FFAF5F +} + +div.highlight .-Color[class*=-C216] { + color: #FFAF87 +} + +div.highlight .-Color[class*=-BGC216] { + background-color: #FFAF87 +} + +div.highlight .-Color[class*=-C217] { + color: #FFAFAF +} + +div.highlight .-Color[class*=-BGC217] { + background-color: #FFAFAF +} + +div.highlight .-Color[class*=-C218] { + color: #FFAFD7 +} + +div.highlight .-Color[class*=-BGC218] { + background-color: #FFAFD7 +} + +div.highlight .-Color[class*=-C219] { + color: #FFAFFF +} + +div.highlight .-Color[class*=-BGC219] { + background-color: #FFAFFF +} + +div.highlight .-Color[class*=-C220] { + color: #FFD700 +} + +div.highlight .-Color[class*=-BGC220] { + background-color: #FFD700 +} + +div.highlight .-Color[class*=-C221] { + color: #FFD75F +} + +div.highlight .-Color[class*=-BGC221] { + background-color: #FFD75F +} + +div.highlight .-Color[class*=-C222] { + color: #FFD787 +} + +div.highlight .-Color[class*=-BGC222] { + background-color: #FFD787 +} + +div.highlight .-Color[class*=-C223] { + color: #FFD7AF +} + +div.highlight .-Color[class*=-BGC223] { + background-color: #FFD7AF +} + +div.highlight .-Color[class*=-C224] { + color: #FFD7D7 +} + +div.highlight .-Color[class*=-BGC224] { + background-color: #FFD7D7 +} + +div.highlight .-Color[class*=-C225] { + color: #FFD7FF +} + +div.highlight .-Color[class*=-BGC225] { + background-color: #FFD7FF +} + +div.highlight .-Color[class*=-C226] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC226] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C227] { + color: #FFFF5F +} + +div.highlight .-Color[class*=-BGC227] { + background-color: #FFFF5F +} + +div.highlight .-Color[class*=-C228] { + color: #FFFF87 +} + +div.highlight .-Color[class*=-BGC228] { + background-color: #FFFF87 +} + +div.highlight .-Color[class*=-C229] { + color: #FFFFAF +} + +div.highlight .-Color[class*=-BGC229] { + background-color: #FFFFAF +} + +div.highlight .-Color[class*=-C230] { + color: #FFFFD7 +} + +div.highlight .-Color[class*=-BGC230] { + background-color: #FFFFD7 +} + +div.highlight .-Color[class*=-C231] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC231] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C232] { + color: #080808 +} + +div.highlight .-Color[class*=-BGC232] { + background-color: #080808 +} + +div.highlight .-Color[class*=-C233] { + color: #121212 +} + +div.highlight .-Color[class*=-BGC233] { + background-color: #121212 +} + +div.highlight .-Color[class*=-C234] { + color: #1C1C1C +} + +div.highlight .-Color[class*=-BGC234] { + background-color: #1C1C1C +} + +div.highlight .-Color[class*=-C235] { + color: #262626 +} + +div.highlight .-Color[class*=-BGC235] { + background-color: #262626 +} + +div.highlight .-Color[class*=-C236] { + color: #303030 +} + +div.highlight .-Color[class*=-BGC236] { + background-color: #303030 +} + +div.highlight .-Color[class*=-C237] { + color: #3A3A3A +} + +div.highlight .-Color[class*=-BGC237] { + background-color: #3A3A3A +} + +div.highlight .-Color[class*=-C238] { + color: #444444 +} + +div.highlight .-Color[class*=-BGC238] { + background-color: #444444 +} + +div.highlight .-Color[class*=-C239] { + color: #4E4E4E +} + +div.highlight .-Color[class*=-BGC239] { + background-color: #4E4E4E +} + +div.highlight .-Color[class*=-C240] { + color: #585858 +} + +div.highlight .-Color[class*=-BGC240] { + background-color: #585858 +} + +div.highlight .-Color[class*=-C241] { + color: #626262 +} + +div.highlight .-Color[class*=-BGC241] { + background-color: #626262 +} + +div.highlight .-Color[class*=-C242] { + color: #6C6C6C +} + +div.highlight .-Color[class*=-BGC242] { + background-color: #6C6C6C +} + +div.highlight .-Color[class*=-C243] { + color: #767676 +} + +div.highlight .-Color[class*=-BGC243] { + background-color: #767676 +} + +div.highlight .-Color[class*=-C244] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC244] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C245] { + color: #8A8A8A +} + +div.highlight .-Color[class*=-BGC245] { + background-color: #8A8A8A +} + +div.highlight .-Color[class*=-C246] { + color: #949494 +} + +div.highlight .-Color[class*=-BGC246] { + background-color: #949494 +} + +div.highlight .-Color[class*=-C247] { + color: #9E9E9E +} + +div.highlight .-Color[class*=-BGC247] { + background-color: #9E9E9E +} + +div.highlight .-Color[class*=-C248] { + color: #A8A8A8 +} + +div.highlight .-Color[class*=-BGC248] { + background-color: #A8A8A8 +} + +div.highlight .-Color[class*=-C249] { + color: #B2B2B2 +} + +div.highlight .-Color[class*=-BGC249] { + background-color: #B2B2B2 +} + +div.highlight .-Color[class*=-C250] { + color: #BCBCBC +} + +div.highlight .-Color[class*=-BGC250] { + background-color: #BCBCBC +} + +div.highlight .-Color[class*=-C251] { + color: #C6C6C6 +} + +div.highlight .-Color[class*=-BGC251] { + background-color: #C6C6C6 +} + +div.highlight .-Color[class*=-C252] { + color: #D0D0D0 +} + +div.highlight .-Color[class*=-BGC252] { + background-color: #D0D0D0 +} + +div.highlight .-Color[class*=-C253] { + color: #DADADA +} + +div.highlight .-Color[class*=-BGC253] { + background-color: #DADADA +} + +div.highlight .-Color[class*=-C254] { + color: #E4E4E4 +} + +div.highlight .-Color[class*=-BGC254] { + background-color: #E4E4E4 +} + +div.highlight .-Color[class*=-C255] { + color: #EEEEEE +} + +div.highlight .-Color[class*=-BGC255] { + background-color: #EEEEEE +} diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..84ab303 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..b08d58c --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,620 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + "Search finished, found ${resultCount} page(s) matching the search query." + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/_static/sphinx_lesson.css b/_static/sphinx_lesson.css new file mode 100644 index 0000000..14b20c6 --- /dev/null +++ b/_static/sphinx_lesson.css @@ -0,0 +1,51 @@ +/* sphinx_lesson.css */ + +body.wy-body-for-nav img.with-border { + border: 2px solid; +} + +.rst-content .admonition-no-content { + padding-bottom: 0px; +} + +.rst-content .demo > .admonition-title::before { + content: "\01F440"; /* Eyes */ } +.rst-content .type-along > .admonition-title::before { + content: "\02328\0FE0F"; /* Keyboard */ } +.rst-content .exercise > .admonition-title::before { + content: "\0270D\0FE0F"; /* Hand */ } +.rst-content .solution > .admonition-title::before { + content: "\02714\0FE0E"; /* Check mark */ } +.rst-content .homework > .admonition-title::before { + content: "\01F4DD"; /* Memo */ } +.rst-content .discussion > .admonition-title::before { + content: "\01F4AC"; /* Speech balloon */ } +.rst-content .questions > .admonition-title::before { + content: "\02753\0FE0E"; /* Question mark */ } +.rst-content .prerequisites > .admonition-title::before { + content: "\02699"; /* Gear */ } +.rst-content .seealso > .admonition-title::before { + content: "\027A1\0FE0E"; /* Question mark */ } + + +/* instructor-note */ +.rst-content .instructor-note { + background: #e7e7e7; +} +.rst-content .instructor-note > .admonition-title { + background: #6a6a6a; +} +.rst-content .instructor-note > .admonition-title::before { + content: ""; +} + + +/* sphinx_toggle_button, make the font white */ +.rst-content .toggle.admonition button.toggle-button { + color: white; +} + +/* sphinx-togglebutton, remove underflow when toggled to hidden mode */ +.rst-content .admonition.toggle-hidden { + padding-bottom: 0px; +} diff --git a/_static/sphinx_rtd_theme_ext_color_contrast.css b/_static/sphinx_rtd_theme_ext_color_contrast.css new file mode 100644 index 0000000..e68feb8 --- /dev/null +++ b/_static/sphinx_rtd_theme_ext_color_contrast.css @@ -0,0 +1,47 @@ +/* The following are for web accessibility of sphinx_rtd_theme: they + * solve some of the most frequent contrast issues. Remove when this + * solved: + * https://github.com/readthedocs/sphinx_rtd_theme/issues/971 + */ +/* background: #fcfcfc, note boxes #E7F2FA */ +a { color: #2573A7; } /* original #2980B9, #1F5C84; */ +body { color: #242424; } /* original #404040, #383838 */ +.wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a { + color: #ffffff; +} /* original #fcfcfc */ +footer { color: #737373; } /* original gray=#808080*/ +footer span.commit code, footer span.commit .rst-content tt, .rst-content footer span.commit tt { + color: #737373; +} /* original gray=#808080*/ +.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal { + color: #AB2314; +} +/* Sidebar background */ +.wy-side-nav-search { background-color: #277CB4;} + +/* Same, but for pygments */ +.highlight .ch { color: #3E7A89; } /* #! line */ +.highlight .c1 { color: #3E7A89; } /* also comments */ +.highlight .nv { color: #AD3ECC; } /* variable */ +.highlight .gp { color: #B45608; } /* prompt character, $*/ +.highlight .si { color: #3975B1; } /* ${} variable text */ +.highlight .nc { color: #0C78A7; } + +/* Sphinx admonitions */ +/* warning */ +.wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .rst-content .wy-alert-warning.admonition .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title, .rst-content .wy-alert-warning.admonition .admonition-title { + background: #B15E16; } +/* important */ +.wy-alert.wy-alert-success .wy-alert-title, .rst-content .wy-alert-success.note .wy-alert-title, .rst-content .wy-alert-success.attention .wy-alert-title, .rst-content .wy-alert-success.caution .wy-alert-title, .rst-content .wy-alert-success.danger .wy-alert-title, .rst-content .wy-alert-success.error .wy-alert-title, .rst-content .hint .wy-alert-title, .rst-content .important .wy-alert-title, .rst-content .tip .wy-alert-title, .rst-content .wy-alert-success.warning .wy-alert-title, .rst-content .wy-alert-success.seealso .wy-alert-title, .rst-content .wy-alert-success.admonition-todo .wy-alert-title, .rst-content .wy-alert-success.admonition .wy-alert-title, .wy-alert.wy-alert-success .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-success .admonition-title, .rst-content .wy-alert-success.note .admonition-title, .rst-content .wy-alert-success.attention .admonition-title, .rst-content .wy-alert-success.caution .admonition-title, .rst-content .wy-alert-success.danger .admonition-title, .rst-content .wy-alert-success.error .admonition-title, .rst-content .hint .admonition-title, .rst-content .important .admonition-title, .rst-content .tip .admonition-title, .rst-content .wy-alert-success.warning .admonition-title, .rst-content .wy-alert-success.seealso .admonition-title, .rst-content .wy-alert-success.admonition-todo .admonition-title, .rst-content .wy-alert-success.admonition .admonition-title { + background: #12826C; } +/* seealso, note, etc */ +.wy-alert.wy-alert-info .wy-alert-title, .rst-content .note .wy-alert-title, .rst-content .wy-alert-info.attention .wy-alert-title, .rst-content .wy-alert-info.caution .wy-alert-title, .rst-content .wy-alert-info.danger .wy-alert-title, .rst-content .wy-alert-info.error .wy-alert-title, .rst-content .wy-alert-info.hint .wy-alert-title, .rst-content .wy-alert-info.important .wy-alert-title, .rst-content .wy-alert-info.tip .wy-alert-title, .rst-content .wy-alert-info.warning .wy-alert-title, .rst-content .seealso .wy-alert-title, .rst-content .wy-alert-info.admonition-todo .wy-alert-title, .rst-content .wy-alert-info.admonition .wy-alert-title, .wy-alert.wy-alert-info .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-info .admonition-title, .rst-content .note .admonition-title, .rst-content .wy-alert-info.attention .admonition-title, .rst-content .wy-alert-info.caution .admonition-title, .rst-content .wy-alert-info.danger .admonition-title, .rst-content .wy-alert-info.error .admonition-title, .rst-content .wy-alert-info.hint .admonition-title, .rst-content .wy-alert-info.important .admonition-title, .rst-content .wy-alert-info.tip .admonition-title, .rst-content .wy-alert-info.warning .admonition-title, .rst-content .seealso .admonition-title, .rst-content .wy-alert-info.admonition-todo .admonition-title, .rst-content .wy-alert-info.admonition .admonition-title { + background: #277CB4; } +/* error, danger */ +.rst-content .danger .admonition-title, .rst-content .danger .wy-alert-title, .rst-content .error .admonition-title, .rst-content .error .wy-alert-title, .rst-content .wy-alert-danger.admonition-todo .admonition-title, .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, .rst-content .wy-alert-danger.admonition .admonition-title, .rst-content .wy-alert-danger.admonition .wy-alert-title, .rst-content .wy-alert-danger.attention .admonition-title, .rst-content .wy-alert-danger.attention .wy-alert-title, .rst-content .wy-alert-danger.caution .admonition-title, .rst-content .wy-alert-danger.caution .wy-alert-title, .rst-content .wy-alert-danger.hint .admonition-title, .rst-content .wy-alert-danger.hint .wy-alert-title, .rst-content .wy-alert-danger.important .admonition-title, .rst-content .wy-alert-danger.important .wy-alert-title, .rst-content .wy-alert-danger.note .admonition-title, .rst-content .wy-alert-danger.note .wy-alert-title, .rst-content .wy-alert-danger.seealso .admonition-title, .rst-content .wy-alert-danger.seealso .wy-alert-title, .rst-content .wy-alert-danger.tip .admonition-title, .rst-content .wy-alert-danger.tip .wy-alert-title, .rst-content .wy-alert-danger.warning .admonition-title, .rst-content .wy-alert-danger.warning .wy-alert-title, .rst-content .wy-alert.wy-alert-danger .admonition-title, .wy-alert.wy-alert-danger .rst-content .admonition-title, .wy-alert.wy-alert-danger .wy-alert-title { + background: #e31704; +} + +/* Generic admonition titles */ +.wy-alert-title, .rst-content .admonition-title { + background: #277CB4; } diff --git a/_static/style.css b/_static/style.css new file mode 100644 index 0000000..59e60ee --- /dev/null +++ b/_static/style.css @@ -0,0 +1,6 @@ +.rst-content .objectives { + background: #fee0d2; +} +.rst-content .objectives > .admonition-title { + background: #fc9272; +} diff --git a/_static/tabs.css b/_static/tabs.css new file mode 100644 index 0000000..957ba60 --- /dev/null +++ b/_static/tabs.css @@ -0,0 +1,89 @@ +.sphinx-tabs { + margin-bottom: 1rem; +} + +[role="tablist"] { + border-bottom: 1px solid #a0b3bf; +} + +.sphinx-tabs-tab { + position: relative; + font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif; + color: #1D5C87; + line-height: 24px; + margin: 0; + font-size: 16px; + font-weight: 400; + background-color: rgba(255, 255, 255, 0); + border-radius: 5px 5px 0 0; + border: 0; + padding: 1rem 1.5rem; + margin-bottom: 0; +} + +.sphinx-tabs-tab[aria-selected="true"] { + font-weight: 700; + border: 1px solid #a0b3bf; + border-bottom: 1px solid white; + margin: -1px; + background-color: white; +} + +.sphinx-tabs-tab:focus { + z-index: 1; + outline-offset: 1px; +} + +.sphinx-tabs-panel { + position: relative; + padding: 1rem; + border: 1px solid #a0b3bf; + margin: 0px -1px -1px -1px; + border-radius: 0 0 5px 5px; + border-top: 0; + background: white; +} + +.sphinx-tabs-panel.code-tab { + padding: 0.4rem; +} + +.sphinx-tab img { + margin-bottom: 24 px; +} + +/* Dark theme preference styling */ + +@media (prefers-color-scheme: dark) { + body[data-theme="auto"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); + } + + body[data-theme="auto"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); + } + + body[data-theme="auto"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 1px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); + } +} + +/* Explicit dark theme styling */ + +body[data-theme="dark"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); +} + +body[data-theme="dark"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); +} + +body[data-theme="dark"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 2px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); +} diff --git a/_static/tabs.js b/_static/tabs.js new file mode 100644 index 0000000..48dc303 --- /dev/null +++ b/_static/tabs.js @@ -0,0 +1,145 @@ +try { + var session = window.sessionStorage || {}; +} catch (e) { + var session = {}; +} + +window.addEventListener("DOMContentLoaded", () => { + const allTabs = document.querySelectorAll('.sphinx-tabs-tab'); + const tabLists = document.querySelectorAll('[role="tablist"]'); + + allTabs.forEach(tab => { + tab.addEventListener("click", changeTabs); + }); + + tabLists.forEach(tabList => { + tabList.addEventListener("keydown", keyTabs); + }); + + // Restore group tab selection from session + const lastSelected = session.getItem('sphinx-tabs-last-selected'); + if (lastSelected != null) selectNamedTabs(lastSelected); +}); + +/** + * Key focus left and right between sibling elements using arrows + * @param {Node} e the element in focus when key was pressed + */ +function keyTabs(e) { + const tab = e.target; + let nextTab = null; + if (e.keyCode === 39 || e.keyCode === 37) { + tab.setAttribute("tabindex", -1); + // Move right + if (e.keyCode === 39) { + nextTab = tab.nextElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.firstElementChild; + } + // Move left + } else if (e.keyCode === 37) { + nextTab = tab.previousElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.lastElementChild; + } + } + } + + if (nextTab !== null) { + nextTab.setAttribute("tabindex", 0); + nextTab.focus(); + } +} + +/** + * Select or deselect clicked tab. If a group tab + * is selected, also select tab in other tabLists. + * @param {Node} e the element that was clicked + */ +function changeTabs(e) { + // Use this instead of the element that was clicked, in case it's a child + const notSelected = this.getAttribute("aria-selected") === "false"; + const positionBefore = this.parentNode.getBoundingClientRect().top; + const notClosable = !this.parentNode.classList.contains("closeable"); + + deselectTabList(this); + + if (notSelected || notClosable) { + selectTab(this); + const name = this.getAttribute("name"); + selectNamedTabs(name, this.id); + + if (this.classList.contains("group-tab")) { + // Persist during session + session.setItem('sphinx-tabs-last-selected', name); + } + } + + const positionAfter = this.parentNode.getBoundingClientRect().top; + const positionDelta = positionAfter - positionBefore; + // Scroll to offset content resizing + window.scrollTo(0, window.scrollY + positionDelta); +} + +/** + * Select tab and show associated panel. + * @param {Node} tab tab to select + */ +function selectTab(tab) { + tab.setAttribute("aria-selected", true); + + // Show the associated panel + document + .getElementById(tab.getAttribute("aria-controls")) + .removeAttribute("hidden"); +} + +/** + * Hide the panels associated with all tabs within the + * tablist containing this tab. + * @param {Node} tab a tab within the tablist to deselect + */ +function deselectTabList(tab) { + const parent = tab.parentNode; + const grandparent = parent.parentNode; + + Array.from(parent.children) + .forEach(t => t.setAttribute("aria-selected", false)); + + Array.from(grandparent.children) + .slice(1) // Skip tablist + .forEach(panel => panel.setAttribute("hidden", true)); +} + +/** + * Select grouped tabs with the same name, but no the tab + * with the given id. + * @param {Node} name name of grouped tab to be selected + * @param {Node} clickedId id of clicked tab + */ +function selectNamedTabs(name, clickedId=null) { + const groupedTabs = document.querySelectorAll(`.sphinx-tabs-tab[name="${name}"]`); + const tabLists = Array.from(groupedTabs).map(tab => tab.parentNode); + + tabLists + .forEach(tabList => { + // Don't want to change the tabList containing the clicked tab + const clickedTab = tabList.querySelector(`[id="${clickedId}"]`); + if (clickedTab === null ) { + // Select first tab with matching name + const tab = tabList.querySelector(`.sphinx-tabs-tab[name="${name}"]`); + deselectTabList(tab); + selectTab(tab); + } + }) +} + +if (typeof exports === 'undefined') { + exports = {}; +} + +exports.keyTabs = keyTabs; +exports.changeTabs = changeTabs; +exports.selectTab = selectTab; +exports.deselectTabList = deselectTabList; +exports.selectNamedTabs = selectNamedTabs; diff --git a/_static/term_role_formatting.css b/_static/term_role_formatting.css new file mode 100644 index 0000000..0b66095 --- /dev/null +++ b/_static/term_role_formatting.css @@ -0,0 +1,4 @@ +/* Make terms bold */ +a.reference span.std-term { + font-weight: bold; +} diff --git a/_static/togglebutton.css b/_static/togglebutton.css new file mode 100644 index 0000000..54a6787 --- /dev/null +++ b/_static/togglebutton.css @@ -0,0 +1,160 @@ +/** + * Admonition-based toggles + */ + +/* Visibility of the target */ +.admonition.toggle .admonition-title ~ * { + transition: opacity .3s, height .3s; +} + +/* Toggle buttons inside admonitions so we see the title */ +.admonition.toggle { + position: relative; +} + +/* Titles should cut off earlier to avoid overlapping w/ button */ +.admonition.toggle .admonition-title { + padding-right: 25%; + cursor: pointer; +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:hover { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 1%); +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:active { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 3%); +} + +/* Remove extra whitespace below the admonition title when hidden */ +.admonition.toggle-hidden { + padding-bottom: 0; +} + +.admonition.toggle-hidden .admonition-title { + margin-bottom: 0; +} + +/* hides all the content of a page until de-toggled */ +.admonition.toggle-hidden .admonition-title ~ * { + height: 0; + margin: 0; + opacity: 0; + visibility: hidden; +} + +/* General button style and position*/ +button.toggle-button { + /** + * Background and shape. By default there's no background + * but users can style as they wish + */ + background: none; + border: none; + outline: none; + + /* Positioning just inside the admonition title */ + position: absolute; + right: 0.5em; + padding: 0px; + border: none; + outline: none; +} + +/* Display the toggle hint on wide screens */ +@media (min-width: 768px) { + button.toggle-button.toggle-button-hidden:before { + content: attr(data-toggle-hint); /* This will be filled in by JS */ + font-size: .8em; + align-self: center; + } +} + +/* Icon behavior */ +.tb-icon { + transition: transform .2s ease-out; + height: 1.5em; + width: 1.5em; + stroke: currentColor; /* So that we inherit the color of other text */ +} + +/* The icon should point right when closed, down when open. */ +/* Open */ +.admonition.toggle button .tb-icon { + transform: rotate(90deg); +} + +/* Closed */ +.admonition.toggle button.toggle-button-hidden .tb-icon { + transform: rotate(0deg); +} + +/* With details toggles, we don't rotate the icon so it points right */ +details.toggle-details .tb-icon { + height: 1.4em; + width: 1.4em; + margin-top: 0.1em; /* To center the button vertically */ +} + + +/** + * Details-based toggles. + * In this case, we wrap elements with `.toggle` in a details block. + */ + +/* Details blocks */ +details.toggle-details { + margin: 1em 0; +} + + +details.toggle-details summary { + display: flex; + align-items: center; + cursor: pointer; + list-style: none; + border-radius: .2em; + border-left: 3px solid #1976d2; + background-color: rgb(204 204 204 / 10%); + padding: 0.2em 0.7em 0.3em 0.5em; /* Less padding on left because the SVG has left margin */ + font-size: 0.9em; +} + +details.toggle-details summary:hover { + background-color: rgb(204 204 204 / 20%); +} + +details.toggle-details summary:active { + background: rgb(204 204 204 / 28%); +} + +.toggle-details__summary-text { + margin-left: 0.2em; +} + +details.toggle-details[open] summary { + margin-bottom: .5em; +} + +details.toggle-details[open] summary .tb-icon { + transform: rotate(90deg); +} + +details.toggle-details[open] summary ~ * { + animation: toggle-fade-in .3s ease-out; +} + +@keyframes toggle-fade-in { + from {opacity: 0%;} + to {opacity: 100%;} +} + +/* Print rules - we hide all toggle button elements at print */ +@media print { + /* Always hide the summary so the button doesn't show up */ + details.toggle-details summary { + display: none; + } +} \ No newline at end of file diff --git a/_static/togglebutton.js b/_static/togglebutton.js new file mode 100644 index 0000000..215a7ee --- /dev/null +++ b/_static/togglebutton.js @@ -0,0 +1,187 @@ +/** + * Add Toggle Buttons to elements + */ + +let toggleChevron = ` + + + +`; + +var initToggleItems = () => { + var itemsToToggle = document.querySelectorAll(togglebuttonSelector); + console.log(`[togglebutton]: Adding toggle buttons to ${itemsToToggle.length} items`) + // Add the button to each admonition and hook up a callback to toggle visibility + itemsToToggle.forEach((item, index) => { + if (item.classList.contains("admonition")) { + // If it's an admonition block, then we'll add a button inside + // Generate unique IDs for this item + var toggleID = `toggle-${index}`; + var buttonID = `button-${toggleID}`; + + item.setAttribute('id', toggleID); + if (!item.classList.contains("toggle")){ + item.classList.add("toggle"); + } + // This is the button that will be added to each item to trigger the toggle + var collapseButton = ` + `; + + title = item.querySelector(".admonition-title") + title.insertAdjacentHTML("beforeend", collapseButton); + thisButton = document.getElementById(buttonID); + + // Add click handlers for the button + admonition title (if admonition) + admonitionTitle = document.querySelector(`#${toggleID} > .admonition-title`) + if (admonitionTitle) { + // If an admonition, then make the whole title block clickable + admonitionTitle.addEventListener('click', toggleClickHandler); + admonitionTitle.dataset.target = toggleID + admonitionTitle.dataset.button = buttonID + } else { + // If not an admonition then we'll listen for the button click + thisButton.addEventListener('click', toggleClickHandler); + } + + // Now hide the item for this toggle button unless explicitly noted to show + if (!item.classList.contains("toggle-shown")) { + toggleHidden(thisButton); + } + } else { + // If not an admonition, wrap the block in a
block + // Define the structure of the details block and insert it as a sibling + var detailsBlock = ` +
+ + ${toggleChevron} + ${toggleHintShow} + +
`; + item.insertAdjacentHTML("beforebegin", detailsBlock); + + // Now move the toggle-able content inside of the details block + details = item.previousElementSibling + details.appendChild(item) + item.classList.add("toggle-details__container") + + // Set up a click trigger to change the text as needed + details.addEventListener('click', (click) => { + let parent = click.target.parentElement; + if (parent.tagName.toLowerCase() == "details") { + summary = parent.querySelector("summary"); + details = parent; + } else { + summary = parent; + details = parent.parentElement; + } + // Update the inner text for the proper hint + if (details.open) { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintShow; + } else { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintHide; + } + + }); + + // If we have a toggle-shown class, open details block should be open + if (item.classList.contains("toggle-shown")) { + details.click(); + } + } + }) +}; + +// This should simply add / remove the collapsed class and change the button text +var toggleHidden = (button) => { + target = button.dataset['target'] + var itemToToggle = document.getElementById(target); + if (itemToToggle.classList.contains("toggle-hidden")) { + itemToToggle.classList.remove("toggle-hidden"); + button.classList.remove("toggle-button-hidden"); + } else { + itemToToggle.classList.add("toggle-hidden"); + button.classList.add("toggle-button-hidden"); + } +} + +var toggleClickHandler = (click) => { + // Be cause the admonition title is clickable and extends to the whole admonition + // We only look for a click event on this title to trigger the toggle. + + if (click.target.classList.contains("admonition-title")) { + button = click.target.querySelector(".toggle-button"); + } else if (click.target.classList.contains("tb-icon")) { + // We've clicked the icon and need to search up one parent for the button + button = click.target.parentElement; + } else if (click.target.tagName == "polyline") { + // We've clicked the SVG elements inside the button, need to up 2 layers + button = click.target.parentElement.parentElement; + } else if (click.target.classList.contains("toggle-button")) { + // We've clicked the button itself and so don't need to do anything + button = click.target; + } else { + console.log(`[togglebutton]: Couldn't find button for ${click.target}`) + } + target = document.getElementById(button.dataset['button']); + toggleHidden(target); +} + +// If we want to blanket-add toggle classes to certain cells +var addToggleToSelector = () => { + const selector = ""; + if (selector.length > 0) { + document.querySelectorAll(selector).forEach((item) => { + item.classList.add("toggle"); + }) + } +} + +// Helper function to run when the DOM is finished +const sphinxToggleRunWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} +sphinxToggleRunWhenDOMLoaded(addToggleToSelector) +sphinxToggleRunWhenDOMLoaded(initToggleItems) + +/** Toggle details blocks to be open when printing */ +if (toggleOpenOnPrint == "true") { + window.addEventListener("beforeprint", () => { + // Open the details + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.dataset["togglestatus"] = el.open; + el.open = true; + }); + + // Open the admonitions + document.querySelectorAll(".admonition.toggle.toggle-hidden").forEach((el) => { + console.log(el); + el.querySelector("button.toggle-button").click(); + el.dataset["toggle_after_print"] = "true"; + }); + }); + window.addEventListener("afterprint", () => { + // Re-close the details that were closed + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.open = el.dataset["togglestatus"] == "true"; + delete el.dataset["togglestatus"]; + }); + + // Re-close the admonition toggle buttons + document.querySelectorAll(".admonition.toggle").forEach((el) => { + if (el.dataset["toggle_after_print"] == "true") { + el.querySelector("button.toggle-button").click(); + delete el.dataset["toggle_after_print"]; + } + }); + }); +} diff --git a/branch/main/_images/add-suggestion.png b/branch/main/_images/add-suggestion.png new file mode 100644 index 0000000..0a28544 Binary files /dev/null and b/branch/main/_images/add-suggestion.png differ diff --git a/branch/main/_images/all-checks-failed.png b/branch/main/_images/all-checks-failed.png new file mode 100644 index 0000000..5175684 Binary files /dev/null and b/branch/main/_images/all-checks-failed.png differ diff --git a/branch/main/_images/animation.mp4 b/branch/main/_images/animation.mp4 new file mode 100644 index 0000000..f037fd4 Binary files /dev/null and b/branch/main/_images/animation.mp4 differ diff --git a/branch/main/_images/bare-repository.png b/branch/main/_images/bare-repository.png new file mode 100644 index 0000000..45286f9 Binary files /dev/null and b/branch/main/_images/bare-repository.png differ diff --git a/branch/main/_images/branches.png b/branch/main/_images/branches.png new file mode 100644 index 0000000..2464b1c Binary files /dev/null and b/branch/main/_images/branches.png differ diff --git a/branch/main/_images/check-details.png b/branch/main/_images/check-details.png new file mode 100644 index 0000000..456a247 Binary files /dev/null and b/branch/main/_images/check-details.png differ diff --git a/branch/main/_images/clone-address.png b/branch/main/_images/clone-address.png new file mode 100644 index 0000000..abdbd85 Binary files /dev/null and b/branch/main/_images/clone-address.png differ diff --git a/branch/main/_images/clone-of-fork.png b/branch/main/_images/clone-of-fork.png new file mode 100644 index 0000000..de1f78d Binary files /dev/null and b/branch/main/_images/clone-of-fork.png differ diff --git a/branch/main/_images/clone.png b/branch/main/_images/clone.png new file mode 100644 index 0000000..ad948ba Binary files /dev/null and b/branch/main/_images/clone.png differ diff --git a/branch/main/_images/comment.png b/branch/main/_images/comment.png new file mode 100644 index 0000000..6ae948c Binary files /dev/null and b/branch/main/_images/comment.png differ diff --git a/branch/main/_images/commit-suggestion.png b/branch/main/_images/commit-suggestion.png new file mode 100644 index 0000000..4ec4bb6 Binary files /dev/null and b/branch/main/_images/commit-suggestion.png differ diff --git a/branch/main/_images/create-repository-with-readme.png b/branch/main/_images/create-repository-with-readme.png new file mode 100644 index 0000000..2411834 Binary files /dev/null and b/branch/main/_images/create-repository-with-readme.png differ diff --git a/branch/main/_images/create-repository.png b/branch/main/_images/create-repository.png new file mode 100644 index 0000000..80fb99c Binary files /dev/null and b/branch/main/_images/create-repository.png differ diff --git a/branch/main/_images/draft-pr-wip.png b/branch/main/_images/draft-pr-wip.png new file mode 100644 index 0000000..12181d5 Binary files /dev/null and b/branch/main/_images/draft-pr-wip.png differ diff --git a/branch/main/_images/draft-pr.png b/branch/main/_images/draft-pr.png new file mode 100644 index 0000000..2099349 Binary files /dev/null and b/branch/main/_images/draft-pr.png differ diff --git a/branch/main/_images/files-changed.png b/branch/main/_images/files-changed.png new file mode 100644 index 0000000..7b58a22 Binary files /dev/null and b/branch/main/_images/files-changed.png differ diff --git a/branch/main/_images/fork-after-update.png b/branch/main/_images/fork-after-update.png new file mode 100644 index 0000000..38fc8c6 Binary files /dev/null and b/branch/main/_images/fork-after-update.png differ diff --git a/branch/main/_images/fork.png b/branch/main/_images/fork.png new file mode 100644 index 0000000..e658473 Binary files /dev/null and b/branch/main/_images/fork.png differ diff --git a/branch/main/_images/forkandclone.png b/branch/main/_images/forkandclone.png new file mode 100644 index 0000000..d050183 Binary files /dev/null and b/branch/main/_images/forkandclone.png differ diff --git a/branch/main/_images/gophers.png b/branch/main/_images/gophers.png new file mode 100644 index 0000000..741406b Binary files /dev/null and b/branch/main/_images/gophers.png differ diff --git a/branch/main/_images/history.png b/branch/main/_images/history.png new file mode 100644 index 0000000..a3a0d99 Binary files /dev/null and b/branch/main/_images/history.png differ diff --git a/branch/main/_images/issue-number.png b/branch/main/_images/issue-number.png new file mode 100644 index 0000000..be273a2 Binary files /dev/null and b/branch/main/_images/issue-number.png differ diff --git a/branch/main/_images/leave-comment.png b/branch/main/_images/leave-comment.png new file mode 100644 index 0000000..95e33c2 Binary files /dev/null and b/branch/main/_images/leave-comment.png differ diff --git a/branch/main/_images/license.png b/branch/main/_images/license.png new file mode 100644 index 0000000..ec5cf85 Binary files /dev/null and b/branch/main/_images/license.png differ diff --git a/branch/main/_images/network.png b/branch/main/_images/network.png new file mode 100644 index 0000000..26a6d63 Binary files /dev/null and b/branch/main/_images/network.png differ diff --git a/branch/main/_images/new-repository.png b/branch/main/_images/new-repository.png new file mode 100644 index 0000000..aff7677 Binary files /dev/null and b/branch/main/_images/new-repository.png differ diff --git a/branch/main/_images/owl.png b/branch/main/_images/owl.png new file mode 100644 index 0000000..7f90e9f Binary files /dev/null and b/branch/main/_images/owl.png differ diff --git a/branch/main/_images/pull-request-form.png b/branch/main/_images/pull-request-form.png new file mode 100644 index 0000000..c6c6af8 Binary files /dev/null and b/branch/main/_images/pull-request-form.png differ diff --git a/branch/main/_images/sharing.png b/branch/main/_images/sharing.png new file mode 100644 index 0000000..8368d6b Binary files /dev/null and b/branch/main/_images/sharing.png differ diff --git a/branch/main/_images/stars.png b/branch/main/_images/stars.png new file mode 100644 index 0000000..f4c5cf7 Binary files /dev/null and b/branch/main/_images/stars.png differ diff --git a/branch/main/_images/sync-fork.png b/branch/main/_images/sync-fork.png new file mode 100644 index 0000000..848a054 Binary files /dev/null and b/branch/main/_images/sync-fork.png differ diff --git a/branch/main/_images/unwatch.png b/branch/main/_images/unwatch.png new file mode 100644 index 0000000..020a6c9 Binary files /dev/null and b/branch/main/_images/unwatch.png differ diff --git a/branch/main/_images/upload-files.png b/branch/main/_images/upload-files.png new file mode 100644 index 0000000..fd3ef8c Binary files /dev/null and b/branch/main/_images/upload-files.png differ diff --git a/branch/main/_images/vscode-authorize.png b/branch/main/_images/vscode-authorize.png new file mode 100644 index 0000000..4d45dc2 Binary files /dev/null and b/branch/main/_images/vscode-authorize.png differ diff --git a/branch/main/_images/vscode-publish-branch.png b/branch/main/_images/vscode-publish-branch.png new file mode 100644 index 0000000..323ffb2 Binary files /dev/null and b/branch/main/_images/vscode-publish-branch.png differ diff --git a/branch/main/_images/vscode-publish-to-github1.png b/branch/main/_images/vscode-publish-to-github1.png new file mode 100644 index 0000000..883e756 Binary files /dev/null and b/branch/main/_images/vscode-publish-to-github1.png differ diff --git a/branch/main/_images/vscode-publish-to-github2.png b/branch/main/_images/vscode-publish-to-github2.png new file mode 100644 index 0000000..d0055db Binary files /dev/null and b/branch/main/_images/vscode-publish-to-github2.png differ diff --git a/branch/main/_images/vscode-publish-to-github3.png b/branch/main/_images/vscode-publish-to-github3.png new file mode 100644 index 0000000..0f68d0e Binary files /dev/null and b/branch/main/_images/vscode-publish-to-github3.png differ diff --git a/branch/main/_images/vscode-start.png b/branch/main/_images/vscode-start.png new file mode 100644 index 0000000..e783080 Binary files /dev/null and b/branch/main/_images/vscode-start.png differ diff --git a/branch/main/_sources/collaboration.md.txt b/branch/main/_sources/collaboration.md.txt new file mode 100644 index 0000000..12efbc1 --- /dev/null +++ b/branch/main/_sources/collaboration.md.txt @@ -0,0 +1,13 @@ +(collaboration)= + +# Collaborative version control and code review + +```{toctree} +:maxdepth: 1 + +collaboration/concepts.md +collaboration/same-repository.md +collaboration/code-review.md +collaboration/forking-workflow.md +collaboration/demo-discussion.md +``` diff --git a/branch/main/_sources/collaboration/code-review.md.txt b/branch/main/_sources/collaboration/code-review.md.txt new file mode 100644 index 0000000..4a55b03 --- /dev/null +++ b/branch/main/_sources/collaboration/code-review.md.txt @@ -0,0 +1,144 @@ +(code-review)= + +# Practicing code review + +In this episode we will practice the code review process. We will learn how to +ask for changes in a pull request, how to suggest a change in a pull request, +and how to modify a pull request. + +This will enable research groups to work more collaboratively and to not only +improve the code quality but also to learn from each other. + + +## Exercise + +:::{prereq} Exercise preparation +We can continue in the same exercise repository which we have used in the +previous episode. +::: + +:::{exercise} Exercise: Practicing code review (25 min) + +**Technical requirements**: +- If you create the commits locally: [Being able to authenticate to GitHub](https://coderefinery.github.io/installation/ssh/) + +**What is familiar** from previous lessons: +- Creating a branch. +- Committing a change on the new branch. +- Opening and merging pull requests. + +**What will be new** in this exercise: +- As a reviewer, we will learn how to ask for changes in a pull request. +- As a reviewer, we will learn how to suggest a change in a pull request. +- As a submitter, we will learn how to modify a pull request without closing + the incomplete one and opening a new one. + +**Exercise tasks**: +1. Create a new branch and one or few commits: in these improve something but also + deliberately introduce a typo and also a larger mistake which we will want to fix during the code review. +1. Open a pull request towards the main branch. +1. As a reviewer to somebody else's pull request, ask for an improvement and + also directly suggest a change for the small typo. (Hint: + suggestions are possible through the GitHub web interface, view of + a pull request, "Files changed" view, after selecting some lines. + Look for the "±" button.) +1. As the submitter, learn how to accept the suggested change. (Hint: + GitHub web interface, "Files Changed" view.) +1. As the submitter, improve the pull request without having to close and open + a new one: by adding a new commit to the same branch. (Hint: push + to the branch again.) +1. Once the changes are addressed, merge the pull request. +::: + + +## Help and discussion + +From here on out, we don't give detailed steps to the solution. You +need to combine what you know, and the extra info below, in order to +solve the above. + +### How to ask for changes in a pull request + +Technically, there are at least two common ways to ask for changes in a pull +request. + +Either in the comment field of the pull request: +:::{figure} img/code-review/comment.png +:width: 60% +:class: with-border +:alt: Screenshot of a pull request comment field +:::: + +Or by using the "Review changes": +:::{figure} img/code-review/files-changed.png +:width: 100% +:class: with-border +:alt: Screenshot of a pull request navigating to the "Review changes" tab +:::: + +And always please be kind and constructive in your comments. Remember that the +goal is not gate-keeping but **collaborative learning**. + + +### How to suggest a change in a pull request as a reviewer + +If you see a very small problem that is easy to fix, you can suggest a change +as a reviewer. + +Instead of asking the submitter to tiny problem, you can suggest a change by +clicking on the plus sign next to the line number in the "Files changed" tab: +:::{figure} img/code-review/leave-comment.png +:width: 100% +:class: with-border +:alt: Screenshot of leaving a comment to a line in a pull request +:::: + +Here you can comment on specific lines or even line ranges. + +But now the interesting part is to click on the "Add a suggestion" symbol (the +one that looks like plus and minus). Now you can fix the tiny problem (in this +case a typo) and then click on the "Add single comment" button: +:::{figure} img/code-review/add-suggestion.png +:width: 60% +:class: with-border +:alt: Sequence of clicks to add a suggestion to a line in a pull request +:::: + +The result is this and the submitter can accept the change with a single click: +:::{figure} img/code-review/commit-suggestion.png +:width: 60% +:class: with-border +:alt: Screenshot of a pull request with a suggested change +:::: + +After accepting with "Commit suggestion", the improvement gets added to the +pull request. + + +### How to modify a pull request to address the review comments + +If the reviewer asks for changes, it is not necessary to close the pull request +and later open a new one. It can even be counter-productive to do so: This can +fragment the discussion and the history of the pull request and can make it +harder to understand the context of the changes. + +A much better mechanism to recognize that pull requests are not implemented +from a specific commit to a specific branch, but **always from a branch to a +branch**. + +This means that you can make amendments to the pull request by adding new +commits to the same source branch. This way the pull request will be updated +automatically and the reviewer can see the new changes and comment on them. + +The fact that pull requests are from branch to branch also strongly suggests +that it is a good practice to create a new branch for each pull request. +Otherwise you could accidentally modify an open pull request by adding new +commits to the source branch. + + +## Summary + +- Our process isn't just about code now. It's about discussion and + working together to make the whole process better. +- GitHub discussions and reviewing are quite powerful and can make + small changes easy. diff --git a/branch/main/_sources/collaboration/concepts.md.txt b/branch/main/_sources/collaboration/concepts.md.txt new file mode 100644 index 0000000..c0a7d27 --- /dev/null +++ b/branch/main/_sources/collaboration/concepts.md.txt @@ -0,0 +1,69 @@ +(concepts)= + +# Concepts around collaboration + +```{objectives} +- Be able to decide whether to divide work at the branch level or at the repository level. +``` + + +## Commits, branches, repositories, forks, clones + +- **repository**: The project, contains all data and history (commits, branches, tags). +- **commit**: Snapshot of the project, gets a unique identifier (e.g. `c7f0e8bfc718be04525847fc7ac237f470add76e`). +- **branch**: Independent development line. The main development line is often called `main`. +- **tag**: A pointer to one commit, to be able to refer to it later. Like a [commemorative plaque](https://en.wikipedia.org/wiki/Commemorative_plaque) + that you attach to a particular commit (e.g. `phd-printed` or `paper-submitted`). +- **cloning**: Copying the whole repository to your laptop - the first time. It is not necessary to download each file one by one. +- **forking**: Taking a copy of a repository (which is typically not yours) - your + copy (fork) stays on GitHub/GitLab and you can make changes to your copy. + + +## Cloning a repository + +In order to make a complete copy a whole repository, the `git clone` command +can be used. When cloning, all the files, of all or selected branches, of a +repository are copied in one operation. Cloning of a repository is of relevance +in a few different situations: +* Working on your own, cloning is the operation that you can use to create + multiple instances of a repository on, for instance, a personal computer, a + server, and a supercomputer. +* The parent repository could be a repository that you or your colleague own. A + common use case for cloning is when working together within a smaller team + where everyone has read and write access to the same git repository. +* Alternatively, cloning can be made from a public repository of a code that + you would like to use. Perhaps you have no intention to work on the code, but + would like to stay in tune with the latest developments, also in-between + releases of new versions of the code. + + +```{figure} img/overview/forkandclone.png +:alt: Forking and cloning +:width: 100% +:class: with-border + +Forking and cloning +``` + + +## Forking a repository + +When a fork is made on GitHub/GitLab a complete copy, of all or selected +branches, of the repository is made. The copy will reside under a different +account on GitHub/GitLab. Forking of a repository is of high relevance when +working with a git repository to which you do not have write access. +* In the fork repository commits can be made to the base branch (`main` or + `master`), and to other branches. +* The commits that are made within the branches of the fork repository can be + contributed back to the parent repository by means of pull or merge requests. + + +## Synchronizing changes between repositories + +- We need a mechanism to communicate changes between the repositories. +- We will **pull** or **fetch** updates **from** remote repositories (we will soon discuss the difference between pull and fetch). +- We will **push** updates **to** remote repositories. +- We will learn how to suggest changes within repositories on GitHub and across repositories (**pull request**). +- Repositories that are forked or cloned do not automatically synchronize themselves: + We will learn how to update forks (by pulling from the "central" repository). +- A main difference between cloning a repository and forking a repository is that the former is a general operation for generating copies of a repository to different computers, whereas forking is a particular operation implemented on GitHub/GitLab. diff --git a/branch/main/_sources/collaboration/demo-discussion.md.txt b/branch/main/_sources/collaboration/demo-discussion.md.txt new file mode 100644 index 0000000..d6b4f28 --- /dev/null +++ b/branch/main/_sources/collaboration/demo-discussion.md.txt @@ -0,0 +1,5 @@ +(collaboration-demo-discussion)= + +# Conclict resolution, rebasing, and organizational strategies + +(40 min demo and discussion) diff --git a/branch/main/_sources/collaboration/forking-workflow.md.txt b/branch/main/_sources/collaboration/forking-workflow.md.txt new file mode 100644 index 0000000..f8c254d --- /dev/null +++ b/branch/main/_sources/collaboration/forking-workflow.md.txt @@ -0,0 +1,223 @@ +(forking-workflow)= + +# How to contribute changes to repositories that belong to others + +In this episode we prepare you to suggest and contribute changes to +repositories that belong to others. These might be open source projects that +you use in your work. + +We will see how Git and services like GitHub or GitLab can be used to suggest +modification without having to ask for write access to the repository and +accept modifications without having to grant write access to others. + + +## Exercise + +:::{prereq} Exercise preparation +- The exercise repository is now different: + (note the `-forking-exercise`). +- First **fork** the exercise repository to your GitHub account. +- Then **clone your fork** to your computer (if you wish to work locally). +- Double-check that you have forked the correct repository. +::: + +:::{exercise} Exercise: Collaborating within the same repository (25 min) + +**Technical requirements**: +- If you create the commits locally: [Being able to authenticate to GitHub](https://coderefinery.github.io/installation/ssh/) + +**What is familiar** from previous lessons: +- Forking a repository. +- Creating a branch. +- Committing a change on the new branch. +- Opening and merging pull requests. + +**What will be new** in this exercise: +- Opening a pull request towards the upstream repository. +- Pull requests can be coupled with automated testing. +- Learning that your fork can get out of date. +- After the pull requests are merged, updating your fork with the changes. +- Learn how to approach other people's repositories with ideas, changes, and requests. + +**Exercise tasks**: +1. Open an issue in the upstream exercise repository where you describe the + change you want to make. Take note of the issue number. +1. Create a new branch in your fork of the repository. +1. Make a change to the recipe book on the new branch and in the commit cross-reference the issue you opened. + See the walk-through below for how to do this. +1. Open a pull request towards the upstream repository. +1. The instructor will review and merge the pull requests. + During the review, pay attention to the automated test step (here for + demonstration purposes, we test whether the recipe contains an ingredients + and an instructions sections). +1. After few pull requests are merged, update your fork with the changes. +1. Check that in your fork you can see changes from other people's pull requests. +::: + + +## Help and discussion + + +### Help! I don't have permissions to push my local changes + +Maybe you see an error like this one: +```text +Please make sure you have the correct access rights +and the repository exists. +``` + +Or like this one: +```text +failed to push some refs to workshop-material/recipe-book-forking-exercise.git +``` + +In this case you probably try to push the changes not to your fork but to the original repository +and in this exercise you do not have write access to the original repository. + +The simpler solution is to clone again but this time your fork. + +:::{solution} Recovery + +But if you want to keep your local changes, you can change the remote URL to point to your fork. +Check where your remote points to with `git remote --verbose`. + +It should look like this (replace `USER` with your GitHub username): +```console +$ git remote --verbose + +origin git@github.com:USER/recipe-book-forking-exercise.git (fetch) +origin git@github.com:USER/recipe-book-forking-exercise.git (push) +``` + +It should **not** look like this: +```console +$ git remote --verbose + +origin git@github.com:workshop-material/recipe-book-forking-exercise.git (fetch) +origin git@github.com:workshop-material/recipe-book-forking-exercise.git (push) +``` + +In this case you can adjust "origin" to point to your fork with: +```console +$ git remote set-url origin git@github.com:USER/recipe-book-forking-exercise.git +``` +::: + + +### Opening a pull request towards the upstream repository + +We have learned in the previous episode that pull requests are always from +branch to branch. But the branch can be in a different repository. + +When you open a pull request in a fork, by default GitHub will suggest to +direct it towards the default branch of the upstream repository. + +This can be changed and it should always be verified, but in this case this is +exactly what we want to do, from fork towards upstream: +:::{figure} img/forking-workflow/pull-request-form.png +:width: 100% +:class: with-border +:alt: Screenshot of a pull request from fork towards upstream +:::: + + +### Pull requests can be coupled with automated testing + +We added an automated test here just for fun and so that you see that this is +possible to do. + +In this exercise, the test is silly. It will check whether the recipe contains +both an ingredients and an instructions section. + +In this example the test failed: +:::{figure} img/forking-workflow/all-checks-failed.png +:width: 60% +:class: with-border +:alt: Screenshot of a failed test in a pull request +:::: + +Click on the "Details" link to see the details of the failed test: +:::{figure} img/forking-workflow/check-details.png +:width: 60% +:class: with-border +:alt: Screenshot of details why the test failed +:::: + +**How can this be useful?** +- The project can define what kind of tests are expected to pass before a pull + request can be merged. +- The reviewer can see the results of the tests, without having to run them + locally. + +**How does it work?** +- We added a GitHub Actions workflow + to automatically run on each push or pull request towards the `main` branch. + +What tests or steps can you image for your project to run automatically with +each pull request? + + +### How to update your fork with changes from upstream + +This used to be difficult but now it is two mouse clicks. + +Navigate to your fork and notice how GitHub tells you that your fork is behind. +In my case, it is 9 commits behind upstream. To fix this, click on "Sync fork" +and then "Update branch": +:::{figure} img/forking-workflow/sync-fork.png +:width: 80% +:class: with-border +:alt: Screenshot on GitHub fork page showing that the fork is behind +:::: + +After the update my "branch is up to date" with the upstream repository: +:::{figure} img/forking-workflow/fork-after-update.png +:width: 80% +:class: with-border +:alt: Screenshot on GitHub after fork has been updated +:::: + + +### How to approach other people’s repositories with ideas, changes, and requests + +**Contributing very minor changes** +- Clone or fork+clone repository +- Create a branch +- Commit and push change +- Open a pull request or merge request + +**If you observe an issue and have an idea how to fix it** +- Open an issue in the repository you wish to contribute to +- Describe the problem +- If you have a suggestion on how to fix it, describe your suggestion +- Possibly **discuss and get feedback** +- If you are working on the fix, indicate it in the issue so that others know that somebody is working on it and who is working on it +- Submit your fix as pull request or merge request which references/closes the issue + +:::{admonition} Motivation +- **Inform others about an observed problem** +- Make it clear whether this issue is up for grabs or already being worked on +::: + +**If you have an idea for a new feature** +- Open an issue in the repository you wish to contribute to +- In the issue, write a short proposal for your suggested change or new feature +- Motivate why and how you wish to do this +- Also indicate where you are unsure and where you would like feedback +- **Discuss and get feedback before you code** +- Once you start coding, indicate that you are working on it +- Once you are done, submit your new feature as pull request or merge request which references/closes the issue/proposal + +:::{admonition} Motivation +- **Get agreement and feedback before writing 5000 lines of code** which might be rejected +- If we later wonder why something was done, we have the issue/proposal as + reference and can read up on the reasoning behind a code change +::: + + +## Summary + +- This forking workflow lets you propose changes to repositories for + which **you have no write access**. +- This is the way that much modern open-source software works. +- You can now contribute to any project you can view. diff --git a/branch/main/_sources/collaboration/same-repository.md.txt b/branch/main/_sources/collaboration/same-repository.md.txt new file mode 100644 index 0000000..1a69d1d --- /dev/null +++ b/branch/main/_sources/collaboration/same-repository.md.txt @@ -0,0 +1,280 @@ +(same-repository)= + +# Collaborating within the same repository + +In this episode, we will learn how to collaborate within the same repository. +We will learn how to cross-reference issues and pull requests, how to review +pull requests, and how to use draft pull requests. + +This exercise will form a good basis for collaboration that is suitable for +most research groups. + +:::{note} +When you read or hear **pull request**, please think of a **change proposal**. +::: + + +## Exercise + +In this exercise, we will contribute to a repository via a **pull request**. +This means that you propose some change, and then it is accepted (or not). + +::::::{prereq} Exercise preparation + +- **First we need to get access** to the [exercise repository](https://github.com/workshop-material/recipe-book) to which we will + contribute. + - Instructor collects GitHub usernames from learners and adds them as collaborators to the exercise repository + (Settings -> Collaborators and teams -> Manage access -> Add people). + +- **Don't forget to accept the invitation** + - Check + - Alternatively check the inbox for the email account you registered with + GitHub. GitHub emails you an invitation link, but if you don't receive it + you can go to your GitHub notifications in the top right corner. The + maintainer can also "copy invite link" and share it within the group. + +- **Watching and unwatching repositories** + - Now that you are a collaborator, you get notified about new issues and pull + requests via email. + - If you do not wish this, you can "unwatch" a repository (top of + the project page). + - However, we recommend watching repositories you are interested + in. You can learn things from experts just by watching the + activity that come through a popular project. + :::{figure} img/unwatch.png + :alt: Unwatching a repository + + Unwatch a repository by clicking "Unwatch" in the repository view, + then "Participating and @mentions" - this way, you will get + notifications about your own interactions. + ::: +:::::: + +:::{exercise} Exercise: Collaborating within the same repository (25 min) + +**Technical requirements** (from installation instructions): +- If you create the commits locally: [Being able to authenticate to GitHub](https://coderefinery.github.io/installation/ssh/) + +**What is familiar** from the previous workshop day (not repeated here): +- Cloning a repository. +- Creating a branch. +- Committing a change on the new branch. +- Submit a pull request towards the main branch. + +**What will be new** in this exercise: +- If you create the changes locally, you will need to **push** them to the remote repository. +- Learning what a protected branch is and how to modify a protected branch: using a pull request. +- Cross-referencing issues and pull requests. +- Practice to review a pull request. +- Learn about the value of draft pull requests. + +**Exercise tasks**: +1. Start in the [exercise + repository](https://github.com/workshop-material/recipe-book) and open an + issue where you describe the change you want to make. Note down the issue + number since you will need it later. +1. Create a new branch. +1. Make a change to the recipe book on the new branch and in the commit + cross-reference the issue you opened (see the walk-through below + for how to do that). +1. Push your new branch (with the new commit) to the repository you + are working on. +1. Open a pull request towards the main branch. +1. Review somebody else's pull request and give constructive feedback. Merge their pull request. +1. Try to create a new branch with some half-finished work and open a draft + pull request. Verify that the draft pull request cannot be merged since it + is not meant to be merged yet. +::: + + +## Solution and hints + +### (1) Opening an issue + +This is done through the GitHub web interface. For example, you could +give the name of the recipe you want to add (so that others don't add +the same one). It is the "Issues" tab. + + +### (2) Create a new branch. + +If on GitHub, you can make the branch in the web interface. + + +### (3) Make a change adding the recipe + +Add a new file with the recipe in it. Commit the file. In the commit +message, include the note about the issue number, saying that this +will close that issue. + + +#### Cross-referencing issues and pull requests + +Each issue and each pull request gets a number and you can cross-reference them. + +When you open an issue, note down the issue number (in this case it is `#2`): +:::{figure} img/same-repository/issue-number.png +:width: 60% +:class: with-border +:alt: Each issue gets a number +::: + +You can reference this issue number in a commit message or in a pull request, like in this +commit message: +```text +this is the new recipe; fixes #2 +``` + +If you forget to do that in your commit message, you can also reference the issue +in the pull request description. And instead of `fixes` you can also use `closes` or `resolves` +or `fix` or `close` or `resolve` (case insensitive). + +Here are all the keywords that GitHub recognizes: + + +Then observe what happens in the issue once your commit gets merged: it will +automatically close the issue and create a link between the issue and the +commit. This is very useful for tracking what changes were made in response to +which issue and to know from when until when precisely the issue was open. + + +### (4) Push to GitHub as a new branch + +Push the branch to the repository. You should end up with a branch +visible in the GitHub web view. + +This is only necessary if you created the changes locally. If you created the +changes directly on GitHub, you can skip this step. + +:::::{tabs} +::::{group-tab} VS Code +In VS Code, you can "publish the branch" to the remote repository by clicking +the cloud icon in the bottom left corner of the window: +:::{figure} img/same-repository/vscode-publish-branch.png +:width: 60% +:class: with-border +:alt: Publishing a branch in VS Code +::: +:::: + +::::{group-tab} Command line +If you have a local branch `my-branch` and you want to push it to the remote +and make it visible there, first verify what your remote is: +```console +$ git remote --verbose + +origin git@github.com:USER/recipe-book.git (fetch) +origin git@github.com:USER/recipe-book.git (push) +``` + +In this case the remote is called `origin` and refers to the address +git@github.com:USER/recipe-book.git. Both can be used +interchangeably. Make sure it points to the right repository, ideally a +repository that you can write to. + +Now that you have a remote, you can push your branch to it: +```console +$ git push origin my-branch +``` +This will create a new branch on the remote repository with the same name as +your local branch. + +You can also do this: +```console +$ git push --set-upstream origin my-branch +``` +This will connect the local branch and the remote branch so that in the future +you can just type `git push` and `git pull` without specifying the branch name +(but this depends on your Git configuration). + + +**Troubleshooting** + +If you don't have a remote yet, you can add it with (adjust `ADDRESS` to your repository address): +```console +$ git remote add origin ADDRESS +``` + +ADDRESS is the part that you copy from here: +:::{figure} img/same-repository/clone-address.png +:width: 60% +:class: with-border +:alt: Copying the clone address from GitHub +::: + +If the remote points to the wrong place, you can change it with: +```console +$ git remote set-url origin NEWADDRESS +``` +:::: +::::: + + +### (5) Open a pull request towards the main branch + +This is done through the GitHub web interface. + + +### (6) Reviewing pull requests + +You review through the GitHub web interface. + +Checklist for reviewing a pull request: +- Be kind, on the other side is a human who has put effort into this. +- Be constructive: if you see a problem, suggest a solution. +- Towards which branch is this directed? +- Is the title descriptive? +- Is the description informative? +- Scroll down to see commits. +- Scroll down to see the changes. +- If you get incredibly many changes, also consider the license or copyright + and ask where all that code is coming from. +- Again, be kind and constructive. +- Later we will learn how to suggest changes directly in the pull request. + +If someone is new, it's often nice to say something encouraging in the +comments before merging (even if it's just "thanks"). If all is good +and there's not much else to say, you could merge directly. + + +### (7) Draft pull requests + +Try to create a draft pull request: +:::{figure} img/same-repository/draft-pr.png +:width: 60% +:class: with-border +:alt: Creating a draft pull request +:::: + +Verify that the draft pull request cannot be merged until it is marked as ready +for review: +:::{figure} img/same-repository/draft-pr-wip.png +:width: 60% +:class: with-border +:alt: Draft pull request cannot be merged +:::: + +Draft pull requests can be useful for: +- **Feedback**: You can open a pull request early to get feedback on your work without + signaling that it is ready to merge. +- **Information**: They can help communicating to others that a change is coming up and in + progress. + + +### What is a protected branch? And how to modify it? + +A protected branch on GitHub or GitLab is a branch that cannot (accidentally) +deleted or force-pushed to. It is also possible to require that a branch cannot +be directly pushed to or modified, but that changes must be submitted via a +pull request. + +To protect a branch in your own repository, go to "Settings" -> "Branches". + + + +### Summary + +- We used all the same pieces that we've learned previously. +- But we successfully contributed to a collaborative project! +- The pull request allowed us to contribute without changing directly: + this is very good when it's not mainly our project. diff --git a/branch/main/_sources/credit.md.txt b/branch/main/_sources/credit.md.txt new file mode 100644 index 0000000..9639997 --- /dev/null +++ b/branch/main/_sources/credit.md.txt @@ -0,0 +1,10 @@ +# Credit + +The following material (all CC-BY) was reused to create this workshop material: +- +- +- +- +- + +(I will also summarize what changes have been made to the original material) diff --git a/branch/main/_sources/documentation.md.txt b/branch/main/_sources/documentation.md.txt new file mode 100644 index 0000000..571c5c3 --- /dev/null +++ b/branch/main/_sources/documentation.md.txt @@ -0,0 +1,384 @@ +(documentation)= + +# Code documentation + +:::{objectives} +- Discuss what makes good documentation. +- Improve the README of your project or our example project. +- Explore Sphinx which is a popular tool to build documentation websites. +- Learn how to leverage GitHub Actions and GitHub Pages to build and deploy documentation. +::: + +:::{instructor-note} +- (30 min) Discussion +- (30 min) Exercise: Set up a Sphinx documentation and add API documentation +- (15 min) Demo: Building documentation with GitHub Actions +::: + + +## Why? 💗✉️ to your future self + +- You will probably use your code in the future and may forget details. +- You may want others to use your code or contribute + (almost impossible without documentation). + + +## In-code documentation + +Not very useful (more commentary than comment): +```python +# now we check if temperature is below -50 +if temperature < -50: + print("ERROR: temperature is too low") +``` + +More useful (explaining **why**): +```python +# we regard temperatures below -50 degrees as measurement errors +if temperature < -50: + print("ERROR: temperature is too low") +``` + +Keeping zombie code "just in case" (rather use version control): +```python +# do not run this code! +# if temperature > 0: +# print("It is warm") +``` + +Emulating version control: +```python +# John Doe: threshold changed from 0 to 15 on August 5, 2013 +if temperature > 15: + print("It is warm") +``` + + +## Many languages allow "docstrings" + +Example (Python): +```python +def kelvin_to_celsius(temp_k: float) -> float: + """ + Converts temperature in Kelvin to Celsius. + + Parameters + ---------- + temp_k : float + temperature in Kelvin + + Returns + ------- + temp_c : float + temperature in Celsius + """ + assert temp_k >= 0.0, "ERROR: negative T_K" + + temp_c = temp_k - 273.15 + + return temp_c +``` + +:::{keypoints} +- Documentation which is only in the source code is not enough. +- Often a README is enough. +- Documentation needs to be kept + **in the same Git repository** as the code since we want it to evolve with + the code. +::: + + +## Often a README is enough - checklist + +- **Purpose** +- Requirements +- Installation instructions +- **Copy-paste-able example to get started** +- Tutorials covering key functionality +- Reference documentation (e.g. API) covering all functionality +- Authors and **recommended citation** +- License +- Contribution guide + +See also the +[JOSS review checklist](https://joss.readthedocs.io/en/latest/review_checklist.html). + + +## What if you need more than a README? + +- Write documentation in + [Markdown (.md)](https://en.wikipedia.org/wiki/Markdown) + or + [reStructuredText (.rst)](https://en.wikipedia.org/wiki/ReStructuredText) + or + [R Markdown (.Rmd)](https://rmarkdown.rstudio.com/) + +- In the **same repository** as the code -> version control and **reproducibility** + +- Use one of many tools to build HTML out of md/rst/Rmd: + [Sphinx](https://sphinx-doc.org), + [MkDocs](https://www.mkdocs.org/), + [Zola](https://www.getzola.org/), [Jekyll](https://jekyllrb.com/), + [Hugo](https://gohugo.io/), RStudio, [knitr](https://yihui.org/knitr/), + [bookdown](https://bookdown.org/), + [blogdown](https://bookdown.org/yihui/blogdown/), ... + +- Deploy the generated HTML to [GitHub Pages](https://pages.github.com/) or + [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) + + +## Diátaxis + +Diátaxis is a systematic approach to technical documentation authoring. + +- Overview: +- How to use Diátaxis **as a guide** to work: + + +## Exercise: Set up a Sphinx documentation + +:::{prereq} Preparation +In this episode we will use the following 5 packages which we installed +previously as part of the {ref}`conda` or {ref}`venv`: +``` +myst-parser +sphinx +sphinx-rtd-theme +sphinx-autoapi +sphinx-autobuild +``` + +Which repository to use? You have 3 options: +- Clone **your fork** of the planets example repository. +- If you don't have that, you can clone the original repository itself: + +- You can try this with **your own project** and the project does not have to + be a Python project. +::: + +There are at least two ways to get started with Sphinx: +1. Use `sphinx-quickstart` to create a new Sphinx project. +1. **This is what we will do**: Create three files (`doc/conf.py`, `doc/index.md`, and `doc/about.md`) + as starting point and improve from there. + +::::{exercise} Exercise: Set up a Sphinx documentation +1. Create the following three files in your project: + ``` + planets/ + ├── doc/ + │ ├── conf.py + │ ├── index.md + │ └── about.md + └── ... + ``` + + This is `conf.py`: + ```python + project = "planets" + copyright = "2024, Authors" + author = "Authors" + release = "0.1" + + exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + extensions = [ + "myst_parser", # in order to use markdown + ] + + myst_enable_extensions = [ + "colon_fence", # ::: can be used instead of ``` for better rendering + ] + + html_theme = "sphinx_rtd_theme" + ``` + + This is `index.md` (feel free to adjust): + ```markdown + # Our code documentation + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu + fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in + culpa qui officia deserunt mollit anim id est laborum. + + :::{toctree} + :maxdepth: 2 + :caption: Some caption + + about.md + ::: + ``` + + This is `about.md` (feel free to adjust): + ```markdown + # About this code + + Work in progress ... + ``` + +1. Run `sphinx-build` to build the HTML documentation: + ```console + $ sphinx-build doc _build + + ... lots of output ... + The HTML pages are in _build. + ``` + +1. Try to open `_build/index.html` in your browser. + +1. Experiment with adding more content, images, equations, code blocks, ... + - [typography](https://myst-parser.readthedocs.io/en/latest/syntax/typography.html) + - [images](https://myst-parser.readthedocs.io/en/latest/syntax/images_and_figures.html) + - [math and equations](https://myst-parser.readthedocs.io/en/latest/syntax/math.html) + - [code blocks](https://myst-parser.readthedocs.io/en/latest/syntax/code_and_apis.html) +:::: + +There is a lot more you can do: +- This is useful if you want to check the integrity of all internal and external links: + ```console + $ sphinx-build doc -W -b linkcheck _build + ``` +- [sphinx-autobuild](https://pypi.org/project/sphinx-autobuild/) + provides a local web server that will automatically refresh your view + every time you save a file - which makes writing and testing much easier. + + +## Demo: Building documentation with GitHub Actions + +First we need to extend the `environment.yml` file to include the necessary packages: +```{code-block} yaml +--- +emphasize-lines: 9-12 +--- +name: planets +channels: + - conda-forge +dependencies: + - python=3.12 + - numpy + - click + - matplotlib + - myst-parser + - sphinx + - sphinx-rtd-theme + - sphinx-autoapi +``` + +Then we add a GitHub Actions workflow `.github/workflow/sphinx.yml` to build the documentation: +```{code-block} yaml +--- +emphasize-lines: 31 +--- +name: Build documentation + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: write + +jobs: + docs: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases + environment-file: environment.yml + init-shell: bash + cache-environment: true + post-cleanup: 'all' + generate-run-shell: false + + - name: Sphinx build + run: | + sphinx-build doc _build + shell: bash -el {0} + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v4 + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/ + force_orphan: true +``` + +Now: +- Add these two changes to the GitHub repository. +- Go to "Settings" -> "Pages" -> "Branch" -> `gh-pages` -> "Save". +- Look at "Actions" tab and observe the workflow running and hopefully + deploying the website. +- Finally visit the generated site. You find it by clicking the About wheel + icon on top right of your repository. There, select "Use your GitHub Pages + website". +- **This is how we build almost all of our lesson websites** (including this one). +- Another popular place to deploy Sphinx documentation is [ReadTheDocs](https://readthedocs.org/). + + +## Optional: How to auto-generate API documentation in Python + +Add three tiny modifications (highlighted) to `doc/conf.py` to auto-generate API documentation +(this requires the `sphinx-autoapi` package): +```{code-block} python +--- +emphasize-lines: 10, 14, 17 +--- +project = "planets" +copyright = "2024, Authors" +author = "Authors" +release = "0.1" + +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +extensions = [ + "myst_parser", # in order to use markdown + "autoapi.extension", # in order to use markdown +] + +# search this directory for Python files +autoapi_dirs = [".."] + +# ignore this file when generating API documentation +autoapi_ignore = ["*/conf.py"] + +myst_enable_extensions = [ + "colon_fence", # ::: can be used instead of ``` for better rendering +] + +html_theme = "sphinx_rtd_theme" +``` + +Then rebuild the documentation (or push the changes and let GitHub rebuild it) +and you should see a new section "API Reference". + + +## Confused about reStructuredText vs. Markdown vs. MyST? + +- At the beginning there was reStructuredText and Sphinx was built for reStructuredText. +- Independently, Markdown was invented and evolved into a couple of flavors. +- Markdown became more and more popular but was limited compared to reStructuredText. +- Later, [MyST](https://myst-parser.readthedocs.io/en/latest/syntax/typography.html) + was invented to be able to write + something that looks like Markdown but in addition can do everything that + reStructuredText can do with extra directives. + + +## Where to read more + +- [CodeRefinery documentation lesson](https://coderefinery.github.io/documentation/) +- [Sphinx documentation](https://www.sphinx-doc.org/) +- [Sphinx + ReadTheDocs guide](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/index.html) +- For more Markdown functionality, see the [Markdown guide](https://www.markdownguide.org/basic-syntax/). +- For Sphinx additions, see [Sphinx Markup Constructs](https://www.sphinx-doc.org/en/master/markup/index.html). +- [An opinionated guide on documentation in Python](https://docs.python-guide.org/writing/documentation/) diff --git a/branch/main/_sources/example.md.txt b/branch/main/_sources/example.md.txt new file mode 100644 index 0000000..9805632 --- /dev/null +++ b/branch/main/_sources/example.md.txt @@ -0,0 +1,95 @@ +(example-project)= + +# Example project: Simulating the motion of planets + +The [example code](https://github.com/workshop-material/planets) that we will study +is a hopefully simple N-body simulation written in Python. It is not important +or expected that we understand the code in any detail. + +:::{video} video/animation.mp4 +:width: 600 +::: + +The **big picture** is that the code simulates the motion of a number of +planets: +- We can choose the number of planets. +- Each planet starts with a random position, velocity, and mass. +- At each time step, the code calculates the gravitational force between each + pair of planets. +- The forces accelerate each planet, the acceleration modifies the velocity, + the velocity modifies the position of each planet. +- We can choose the number of time steps. +- The units were chosen to make numbers easy to read. + + +## Example run + +:::{instructor-note} +The instructor demonstrates running the code on their computer. +::: + +The code is written to accept **command-line arguments** to specify the number +of planets and the number of time steps. + +We first generate starting data: +```console +$ python generate-data.py --num-planets 10 --output-file initial.csv +``` + +The generated file (initial.csv) could look like this: +``` +px,py,pz,vx,vy,vz,mass +-46.88,-42.51,88.33,-0.86,-0.18,0.55,6.70 +-5.29,17.09,-96.13,0.66,0.45,-0.17,3.51 +83.53,-92.83,-68.77,-0.26,-0.48,0.24,6.84 +-36.31,25.48,64.16,0.85,0.75,-0.56,1.53 +-68.38,-17.21,-97.07,0.60,0.26,0.69,6.63 +-48.37,-48.74,3.92,-0.92,-0.33,-0.93,8.60 +40.53,-75.50,44.18,-0.62,-0.31,-0.53,8.04 +-27.21,10.78,-78.82,-0.09,-0.55,-0.03,5.35 +88.42,-74.95,-45.85,0.81,0.68,0.56,5.36 +39.09,53.12,-59.54,-0.54,0.56,0.07,8.98 +``` + +Then we can simulate their motion (in this case for 20 steps): +```console +$ python simulate.py --num-steps 20 \ + --input-file initial.csv \ + --output-file final.csv +``` + +The `--output-file` (final.csv) is again a CSV file (comma-separated values) +and contains the final positions of all planets. + +It is possible to run on **multiple cores** and to **animate** the result. +Here is an example with 100 planets: +```{code-block} console +--- +emphasize-lines: 7,11 +--- +$ python generate-data.py --num-planets 100 --output-file initial.csv + +$ python simulate.py --num-steps 50 \ + --input-file initial.csv \ + --output-file final.csv \ + --trajectories-file trajectories.npz \ + --num-cores 8 + +$ python animate.py --initial-file initial.csv \ + --trajectories-file trajectories.npz \ + --output-file animation.mp4 +``` + +:::{admonition} Learning goals +- What are the most important steps to make this code **reusable by others** + and **our future selves**? +- Be able to apply these techniques to your own code/script. +::: + +:::{admonition} We will not focus on ... +- ... how the code works internally in detail. +- ... whether this is the most efficient algorithm. +- ... whether the code is numerically stable. +- ... how to code scales with the number of cores. +- ... whether it is portable to other operating systems (we will discuss this later). +::: diff --git a/branch/main/_sources/index.md.txt b/branch/main/_sources/index.md.txt new file mode 100644 index 0000000..647b711 --- /dev/null +++ b/branch/main/_sources/index.md.txt @@ -0,0 +1,142 @@ +# Reproducible research software development using Python + + +## Big-picture goal + +This is a **hands-on course on research software engineering**. In this +workshop we assume that most workshop participants use Python in their work or +a leading a group which uses Python. Therefore, some of the examples will use +Python as the example language. + +We will work with an example project and go through all the steps of a typical +software project. Once we have seen the building blocks, we will try to apply +them to own projects. Workshop participants will receive and also learn to give +constructive code feedback. + + +## Prerequisites + +:::{prereq} Preparation +1. Get a **GitHub account** following [these instructions](https://coderefinery.github.io/installation/github/). +1. You will need a **text editor**. If you don't have a favorite one, we recommend + [VS Code](https://coderefinery.github.io/installation/vscode/). +1. **If you prefer to work in the terminal** and not in VS Code, set up these two (skip this if you use VS Code): + - [Git in the terminal](https://coderefinery.github.io/installation/git-in-terminal/) + - [SSH or HTTPS connection to GitHub from terminal](https://coderefinery.github.io/installation/ssh/) +1. **One of these two software environments** (if you are not sure which one to + choose or have no preference, choose Conda): + - {ref}`conda` + - {ref}`venv` (Snakemake is not available in this environment) +1. **Optional** and only on Linux: [Apptainer](https://apptainer.org/) following + [these instructions](https://apptainer.org/docs/admin/1.3/installation.html#install-from-pre-built-packages). +::: + + +## Schedule + +### Day 1 + +- 13:00-13:30 - **Welcome and introduction** + - Practical information (tools, communication, breaks, etc.) + - Motivation (reproducibility, robustness, distribution, improvement, trust, etc.) + - What will learn and achieve from this course? + - {ref}`example-project` + +- 13:30-14:45 - {ref}`version-control` (1/2) + - {ref}`version-control-motivation` (15 min) + - {ref}`browsing` (30 min) + - {ref}`branching-and-committing` (30 min) + +- 15:00-16:30 - {ref}`version-control` (2/2) + - Merging (40 min) + - Conflict resolution (30 min) + - {ref}`practical-advice` (20 min) + - {ref}`sharing-repositories` (30 min) + +- 16:45-18:00 - {ref}`documentation` + + +### Day 2 + +- 09:00-10:30 - {ref}`collaboration` (1/2) + - {ref}`concepts` (10 min) + - {ref}`same-repository` (40 min) + - {ref}`code-review` (40 min) + +- 10:45-12:15 - {ref}`collaboration` (2/2) + - {ref}`forking-workflow` (50 min) + - {ref}`collaboration-demo-discussion` (40 min) + +- 16:45-18:00 - **Debriefing and Q&A** + - Participants work on their projects + - Together we study actual codes that participants wrote or work on + - Constructively we discuss possible improvements + - Give individual feedback on code projects + + +### Day 3 + +- 09:00-10:30 - {ref}`testing` + +- 10:45-12:15 - {ref}`reusable` + - Tracking dependencies with requirements.txt and environment.yml + - Recording environments in containers + +- 13:00-14:45 - Code quality and good practices + - {ref}`refactoring-demo` (90 min) + - {ref}`refactoring-concepts` (15 min) + +- 15:00-16:30 - {ref}`publishing` + - Licenses + - Publishing the code via Zenodo + - Packaging the code + - Sharing the code via PyPI + +- 16:45-18:00 - **Debriefing and Q&A** + - Participants work on their projects + - Together we study actual codes that participants wrote or work on + - Constructively we discuss possible improvements + - Give individual feedback on code projects + + +### If we have time left + +- {ref}`profiling` + + +```{toctree} +:maxdepth: 1 +:caption: Software environment +:hidden: + +installation/conda +installation/virtual-environment +``` + +```{toctree} +:maxdepth: 1 +:caption: Episodes +:hidden: + +example +version-control +documentation +collaboration +testing +reusable +refactoring-demo +refactoring-concepts +publishing +profiling +``` + +```{toctree} +:maxdepth: 1 +:caption: Reference +:hidden: + +credit +All lessons +CodeRefinery +Reusing +``` diff --git a/branch/main/_sources/installation/conda.md.txt b/branch/main/_sources/installation/conda.md.txt new file mode 100644 index 0000000..830161f --- /dev/null +++ b/branch/main/_sources/installation/conda.md.txt @@ -0,0 +1,82 @@ +(conda)= + +# Conda environment + +A Conda environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located. + +You will need a `environment.yml` file that documents the dependencies: +```yaml +name: coderefinery +channels: + - conda-forge + - bioconda +dependencies: + - python >= 3.10 + - black + - click + - flit + - ipywidgets + - isort + - jupyterlab + - jupyterlab_code_formatter + - jupyterlab-git + - matplotlib + - myst-parser + - nbdime + - numpy + - pandas + - pytest + - pytest-cov + - scalene + - seaborn + - snakemake-minimal + - sphinx + - sphinx-autoapi + - sphinx-autobuild + - sphinx_rtd_theme >= 2.0 + - vulture + - scikit-image +``` + + +## Before you create a virtual environment + +1. Create a new directory for this course. +1. In this directory, create an `environment.yml` file and copy-paste the dependencies above into it. + + +## Choose the tool to manage the environment + +If you are already using one of these tools, please continue using the tool that you like and know. +If you are new to this, **we recommend using Miniconda or Miniforge**. + +- [Anaconda](https://docs.anaconda.com/anaconda/install/) + - Advantages: easy to install, easy to use, good for beginners + - Disadvantages: large download, installs more than we will need, license restrictions +- [Miniconda](https://docs.anaconda.com/miniconda/) + - Advantages: small size, installs only what you need + - Disadvantages: no graphical interface, license restrictions +- [Miniforge](https://github.com/conda-forge/miniforge) + - Advantages: small size, no license restrictions + - Disadvantages: no graphical interface +- [Micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html) + - Advantages: fast, small size + - Disadvantages: no graphical interface +- [Pixi](https://pixi.sh/latest/) + - Advantages: fast and new + - Disadvantages: new and less tested and not documented here + + +## Creating the virtual environment + +1. Open your terminal shell (e.g. Bash or Zsh). +2. Activate `conda` using `conda activate` or `source ~/miniconda3/bin/activate`. +3. Run the following command: + ```console + $ conda env create --file environment.yml + ``` +4. Make sure that you see "coderefinery" in the output when you ask for a list of all available environments: + ```console + $ conda env list + ``` diff --git a/branch/main/_sources/installation/virtual-environment.md.txt b/branch/main/_sources/installation/virtual-environment.md.txt new file mode 100644 index 0000000..69b3231 --- /dev/null +++ b/branch/main/_sources/installation/virtual-environment.md.txt @@ -0,0 +1,77 @@ +(venv)= + +# Virtual environment + +A virtual environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located. + +You will need a `requirements.txt` file that documents the dependencies: +``` +black +click +flit +ipywidgets +isort +jupyterlab +jupyterlab-code-formatter +jupyterlab-git +matplotlib +myst-parser +nbdime +numpy +pandas +pytest +pytest-cov +scalene +seaborn +sphinx +sphinx-autoapi +sphinx-autobuild +sphinx_rtd_theme >= 2.0 +vulture +scikit-image +``` + + +## Before you create a virtual environment + +1. Create a new directory for this course. +1. In this directory, create a `requirements.txt` file and copy-paste the dependencies above into it. + + +## Creating the virtual environment + +Now create a virtual environment in this directory either using [pip and +venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/) +(more traditional and safer) or using [uv](https://docs.astral.sh/uv/) (more modern but also less tested): + +:::::{tabs} + +::::{group-tab} pip and venv +Create a new virtual environment and activate it: +```console +$ python3 -m venv coderefinery-environment +$ source coderefinery-environment/bin/activate +``` + +Install the dependencies into the environment: +```console +$ python3 -m pip install -r requirements.txt +``` +:::: + + +::::{group-tab} uv +Create a new virtual environment and activate it: +```console +$ uv venv coderefinery-environment +$ source coderefinery-environment/bin/activate +``` + +Install the dependencies into the environment: +```console +$ uv pip install -r requirements.txt +``` +:::: + +::::: diff --git a/branch/main/_sources/profiling.md.txt b/branch/main/_sources/profiling.md.txt new file mode 100644 index 0000000..0683e06 --- /dev/null +++ b/branch/main/_sources/profiling.md.txt @@ -0,0 +1,16 @@ +(profiling)= + +# Profiling memory and CPU usage + +Profilers: +- cProfile or profile +- line_profiler +- viztracer +- scalene +- Intel VTune +- PyInstrument +- py-spy +- Pyflame +- timeit + +(here I will add some examples that we can profile together) diff --git a/branch/main/_sources/publishing.md.txt b/branch/main/_sources/publishing.md.txt new file mode 100644 index 0000000..bc3a12e --- /dev/null +++ b/branch/main/_sources/publishing.md.txt @@ -0,0 +1,11 @@ +(publishing)= + +# How to release and publish your code + +Part 1: +- 30 min: Overview over software licenses and code publishing +- 15 min: We add a license, CFF file, and publish to Zenodo (sandbox) + +Part 2: +- 30 min: Creating a Python package with the help of flit +- 15 min: Deploy the package to test-PyPI using GitHub Actions diff --git a/branch/main/_sources/refactoring-concepts.md.txt b/branch/main/_sources/refactoring-concepts.md.txt new file mode 100644 index 0000000..78039ad --- /dev/null +++ b/branch/main/_sources/refactoring-concepts.md.txt @@ -0,0 +1,8 @@ +(refactoring-concepts)= + +# Concepts in refactoring and modular code design + +Topics: +- Pure functions +- Design patterns: functional design vs. object-oriented design +- How to design your code before writing it: document-driven development diff --git a/branch/main/_sources/refactoring-demo.md.txt b/branch/main/_sources/refactoring-demo.md.txt new file mode 100644 index 0000000..e7266bf --- /dev/null +++ b/branch/main/_sources/refactoring-demo.md.txt @@ -0,0 +1,115 @@ +(refactoring-demo)= + +# Demo: From a script towards a workflow + +In this episode we will explore code quality and good practices in Python using +a hands-on approach. We will together build up a small project and improve it +step by step. + +We will start from a relatively simple image processing script which can read a +telescope image of stars and our goal is to **count the number of stars** in +the image. Later we will want to be able to process many such images. + +The (fictional) telescope images look like the one below here ([in this +repository](https://github.com/workshop-material/random-star-images) we can find more): +:::{figure} refactoring/stars.png +:alt: Generated image representing a telescope image of stars +:width: 60% + +Generated image representing a telescope image of stars. +::: + +:::{admonition} Rough plan for this demo +- (15 min) Discuss how we would solve the problem, run example code, and make it work (as part of a Jupyter notebook)? +- (15 min) Refactor the positioning code into a function and a module +- (15 min) Now we wish to process many images - discuss how we would approach this +- (15 min) Introduce CLI and discuss the benefits +- (30 min) From a script to a workflow (using Snakemake) +::: + +:::{solution} Starting point (spoiler alert) + +We can imagine that we pieced together the following code +based on some examples we found online: +```python +import matplotlib.pyplot as plt +from skimage import io, filters, color +from skimage.measure import label, regionprops + + +image = io.imread("stars.png") +sigma = 0.5 + +# if there is a fourth channel (alpha channel), ignore it +rgb_image = image[:, :, :3] +gray_image = color.rgb2gray(rgb_image) + +# apply a gaussian filter to reduce noise +image_smooth = filters.gaussian(gray_image, sigma) + +# threshold the image to create a binary image (bright stars will be white, background black) +thresh = filters.threshold_otsu(image_smooth) +binary_image = image_smooth > thresh + +# label connected regions (stars) in the binary image +labeled_image = label(binary_image) + +# get properties of labeled regions +regions = regionprops(labeled_image) + +# extract star positions (centroids) +star_positions = [region.centroid for region in regions] + +# plot the original image +plt.figure(figsize=(8, 8)) +plt.imshow(image, cmap="gray") + +# overlay star positions with crosses +for star in star_positions: + plt.plot(star[1], star[0], "rx", markersize=5, markeredgewidth=0.1) + +plt.savefig("detected-stars.png", dpi=300) + +print(f"number of stars detected: {len(star_positions)}") +``` +::: + + +## Plan + +Topics we wish to show and discuss: +- Naming (and other) conventions, project organization, modularity +- The value of pure functions and immutability +- Refactoring (explained through examples) +- Auto-formatting and linting with tools like black, vulture, ruff +- Moving a project under Git +- How to document dependencies +- Structuring larger software projects in a modular way +- Command-line interfaces +- Workflows with Snakemake + +We will work together on the code on the big screen, and participants will be +encouraged to give suggestions and ask questions. We will **end up with a Git +repository** which will be shared with workshop participants. + + +## Possible solutions + +:::{solution} Script after some work, with command-line interface (spoiler alert) +This is one possible solution (`count-stars.py`): +```{literalinclude} refactoring/count-stars.py +:language: python +``` +::: + +:::{solution} Snakemake rules which define a workflow (spoiler alert) +This is one possible solution (`snakefile`): +```{literalinclude} refactoring/snakefile +:language: python +``` + +We can process as many images as we like by running: +```console +$ snakemake --cores 4 # adjust to the number of available cores +``` +::: diff --git a/branch/main/_sources/reusable.md.txt b/branch/main/_sources/reusable.md.txt new file mode 100644 index 0000000..c86455a --- /dev/null +++ b/branch/main/_sources/reusable.md.txt @@ -0,0 +1,13 @@ +(reusable)= + +# How to make the project more reusable + +Part 1: +- 15 min: Motivation +- 15 min: Best practices for managing dependencies in Python +- 20 min: Exercise + +Part 2: +- 10 min: Introduction to containers +- 20 min: Demonstration +- 10 min: Discussion diff --git a/branch/main/_sources/testing.md.txt b/branch/main/_sources/testing.md.txt new file mode 100644 index 0000000..da43e9f --- /dev/null +++ b/branch/main/_sources/testing.md.txt @@ -0,0 +1,222 @@ +(testing)= + +# Automated testing + +:::{objectives} +- Know **where to start** in your own project. +- Have an example for how to make the **testing part of code review**. +::: + +:::{instructor-note} +- (15 min) Motivation +- (15 min) End-to-end tests +- (15 min) Pytest +- (15 min) Adding the unit test to GitHub Actions +- (10 min) What else is possible +- (20 min) Exercise +::: + + +## Motivation + +Testing is a way to check that the code does what it is expected to. + +- **Less scary to change code**: tests will tell you whether something broke. +- **Easier for new people** to join. +- Easier for somebody to **revive an old code**. +- **End-to-end test**: run the whole code and compare result to a reference. +- **Unit tests**: test one unit (function or module). Can guide towards better + structured code: complicated code is more difficult to test. + + +## How testing is often taught + +```python +def add(a, b): + return a + b + + +def test_add(): + assert add(1, 2) == 3 +``` + +How this feels: +:::{figure} img/owl.png +:alt: Instruction on how to draw an owl +:width: 50% +:class: with-border + +[Citation needed] +::: + +Instead, we will look at and **discuss a real example** where we test components +from our example project. + + +## Where to start + +Short answer: **Start with an end-to-end test**. + +:::{solution} Longer answer +- A simple script or notebook probably does not need an automated test. + +**If you have nothing yet**: +- Start with an end-to-end test. +- Describe in words how *you* check whether the code still works. +- Translate the words into a script (any language). +- Run the script automatically on every code change. + +**If you want to start with unit-testing**: +- You want to rewrite a function? Start adding a unit test right there first. +::: + + +## End-to-end tests + +- This is our end-to-end test: +- Note how we can run it [on GitHub automatically](https://github.com/workshop-material/planets/blob/813d49a247f36e9c1e10cbe78ecf1ae4b6e971c3/.github/workflows/test.yml#L28). +- Also browse . +- If we have time, we can try to create a pull request which would break the + code and see how the test fails. + +:::{discussion} +Is the [end-to-end test](https://github.com/workshop-material/planets/blob/main/test.sh) +perfect? No. But it's a good starting point. Discuss its limitations. +::: + + +## Pytest + +First we need to add a test function, for instance +for [this function](https://github.com/workshop-material/planets/blob/813d49a247f36e9c1e10cbe78ecf1ae4b6e971c3/simulate.py#L31-L39): +```{code-block} python +--- +emphasize-lines: 12-20 +--- +def force_between_planets(position1, mass1, position2, mass2): + G = 1.0 # gravitational constant + + r = position2 - position1 + distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5 + force_magnitude = G * mass1 * mass2 / distance**2 + force = (r / distance) * force_magnitude + + return force + + +def test_force_between_planets(): + position1 = np.array([0.0, 0.0, 0.0]) + mass1 = 1.0 + position2 = np.array([1.0, 0.0, 0.0]) + mass2 = 2.0 + + force = force_between_planets(position1, mass1, position2, mass2) + + assert np.allclose(force, [2.0, 0.0, 0.0]) +``` + +Let us run the test with: +```console +$ pytest simulate.py +``` + +Explanation: `pytest` will look for functions starting with `test_` in files +and directories given as arguments. It will run them and report the results. + +Now let us try this: +- Commit the test. +- Break the function on purpose and run the test. +- Does the test fail as expected? + + +## Adding the unit test to GitHub Actions + +Our next goal is that we want GitHub to run the unit test +automatically on every change. + +First we need to extend our +[environment.yml](https://github.com/workshop-material/planets/blob/main/environment.yml): +```{code-block} yaml +--- +emphasize-lines: 9 +--- +name: planets +channels: + - conda-forge +dependencies: + - python=3.12 + - numpy + - click + - matplotlib + - pytest +``` + +We also need to extend `.github/workflows/test.yml` (highlighted line): +```{code-block} yaml +--- +emphasize-lines: 29 +--- +name: Test + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases + environment-file: environment.yml + init-shell: bash + cache-environment: true + post-cleanup: 'all' + generate-run-shell: false + + - name: Run tests + run: | + ./test.sh + pytest simulate.py + shell: bash -el {0} +``` + +If we have time, we can try to create a pull request which would break the +code and see how the test fails. + + +## What else is possible + +- The testing above used **example-based** testing. + +- **Test coverage**: how much of the code is traversed by tests? + - Python: [pytest-cov](https://pytest-cov.readthedocs.io/) + - Result can be deployed to services like [Codecov](https://about.codecov.io/) or [Coveralls](https://coveralls.io/). + +- **Property-based** testing: generates arbitrary data matching your specification and checks that your guarantee still holds in that case. + - Python: [hypothesis](https://hypothesis.readthedocs.io/) + +- **Snapshot-based** testing: makes it easier to generate snapshots for regression tests. + - Python: [syrupy](https://syrupy-project.github.io/syrupy/) + +- **Mutation testing**: tests pass -> change a line of code (make a mutant) -> test again and check whether all mutants get "killed". + - Python: [mutmut](https://mutmut.readthedocs.io/) + + +## Exercises + +Experiment with the example project and what we learned above or try it on your +own project: +- Add a unit test. +- Try to run it locally. +- Check whether it fails when you break the corresponding function. +- Try to run it on GitHub Actions. +- Create a pull request which would break the code and see whether the automatic test would catch it. +- Try to design an end-to-end test for your project. diff --git a/branch/main/_sources/version-control.md.txt b/branch/main/_sources/version-control.md.txt new file mode 100644 index 0000000..efcfece --- /dev/null +++ b/branch/main/_sources/version-control.md.txt @@ -0,0 +1,13 @@ +(version-control)= + +# Introduction to version control with Git and GitHub + +```{toctree} +:maxdepth: 1 + +version-control/motivation.md +version-control/browsing.md +version-control/branching-and-committing.md +version-control/practical-advice.md +version-control/sharing.md +``` diff --git a/branch/main/_sources/version-control/branching-and-committing.md.txt b/branch/main/_sources/version-control/branching-and-committing.md.txt new file mode 100644 index 0000000..702e712 --- /dev/null +++ b/branch/main/_sources/version-control/branching-and-committing.md.txt @@ -0,0 +1,339 @@ +(branching-and-committing)= + +# Creating branches and commits + +The first and most basic task to do in Git is **record changes** using +commits. In this part, we will record changes in two +ways: on a **new branch** (which supports multiple lines of work at once), and directly +on the "main" branch (which happens to be the default branch here). + +:::{objectives} +- Record new changes to our own copy of the project. +- Understand adding changes in two separate branches. +- See how to compare different versions. +::: + + +## Background + +- In the previous episode we have browsed an existing **repository** and saw **commits** + and **branches**. +- Each **commit** is a snapshot of the entire project at a certain + point in time and has a unique identifier (**hash**) . +- A **branch** is a line of development, and the `main` branch or `master` branch + are often the default branch in Git. +- A branch in Git is like a **sticky note that is attached to a commit**. When we add + new commits to a branch, the sticky note moves to the new commit. +- **Tags** are a way to mark a specific commit as important, for example a release + version. They are also like a sticky note, but they don't move when new + commits are added. + +:::{figure} img/gophers.png +:alt: Branching explained with a gopher +:width: 100% + +What if two people, at the same time, make two different changes? Git +can merge them together easily. Image created using +([inspiration](https://twitter.com/jay_gee/status/703360688618536960)). +::: + + +## Exercise: Creating branches and commits + +:::{figure} img/illustrations/branches.png +:alt: Illustration of what we want to achieve in this exercise +:width: 60% + +Illustration of what we want to achieve in this exercise. +::: + +:::{exercise} Exercise: Practice creating commits and branches (20 min) +1. First create a new branch and then either add a new file or modify an + existing file and commit the change. Make sure that you now work **on your + copy** of the example repository. In your commit you can share a Git or + programming trick you like. +1. In a new commit, modify the file again. +1. Switch to the `main` branch and create a commit there. +1. Browse the network and locate the commits that you just created ("Insights" -> "Network"). +1. Compare the branch that you created with the `main` branch. Can you find an easy way to see the differences? +1. Can you find a way to compare versions between two arbitrary commits in the repository? +1. Try to rename the branch that you created and then browse the network again. +1. Try to create a tag for one of the commits that you created (on GitHub, + create a "release"). +::: + +The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren't enough - this is by +design. + + +## Solution and walk-through + + +### (1) Create a new branch and a new commit + +:::::{tabs} +::::{group-tab} GitHub +1. Where it says "main" at the top left, click, enter a new branch + name `new-tutorial`, click on the offer to create the new branch + ("Create branch new-tutorial from main"). +1. Make sure you are still on the `new-tutorial` branch (it should say + it at the top), and click "Add file" → "Create new file" from the + upper right. +1. Enter a filename where it says "Name your file...". +1. Share some Git or programming trick you like. +1. Click "Commit changes" +1. Enter a commit message. Then click "Commit + changes". + +You should appear back at the file browser view, and see your +modification there. +:::: + +::::{group-tab} VS Code +1. Make sure that you are on the main branch. +1. Version control button on left sidebar → Three dots in upper right of source control → Branch → Create branch. +1. VS Code automatically switches to the new branch. +4. Create a new file. +4. In the version control sidebar, click the `+` sign to add the file for the next commit. +4. Enter a brief message and click "Commit". +:::: + +::::{group-tab} Command line +Create a new branch called `new-tutorial` from `main` and switch to it: +```console +$ git switch --create new-tutorial main +``` + +Then create the new file. Finally add and commit the file: +```console +$ git add tutorial.md # or a different file name +$ git commit -m "sharing a programming trick" +``` +:::: +::::: + + +### (2) Modify the file again with a new commit + +:::::{tabs} +::::{group-tab} GitHub +This is similar to before, but we click on the existing file to +modify. + +1. Click on the file you added or modified previously. +2. Click the edit button, the pencil icon at top-right. +3. Follow the "Commit changes" instructions as in the previous step. +:::: + +::::{group-tab} VS Code +Repeat as in the previous step. +:::: + +::::{group-tab} Command line +Modify the file. Then commit the new change: +```console +$ git add tutorial.md +$ git commit -m "short summary of the change" +``` + +Make sure to replace "short summary of the change" with a meaningful commit +message. +:::: +::::: + + +### (3) Switch to the main branch and create a commit there + +:::::{tabs} +::::{group-tab} GitHub +1. Go back to the main repository page (your user's page). +1. In the branch switch view (top left above the file view), switch to + `main`. +1. Modify another file that already exists, following the pattern + from above. +:::: + +::::{group-tab} VS Code +Use the branch selector at the bottom to switch back to the main branch. Repeat the same steps as above, +but this time modify a different file. +:::: + +::::{group-tab} Command line +First switch to the `main` branch: +```console +$ git switch main +``` + +Then modify a file. Finally `git add` and then commit the change: +```console +$ git commit -m "short summary of the change" +``` +:::: +::::: + + +### (4) Browse the commits you just made + +Let's look at what we did. Now, the `main` and `new-tutorial` branches +have diverged: both have some modifications. Try to find the commits +you created. + +:::::{tabs} +::::{group-tab} GitHub +Insights tab → Network view (just like we have done before). +:::: + +::::{group-tab} VS Code +This requires an extension. Opening the VS Code terminal lets you use the +command line method (View → Terminal will open a terminal at bottom). This is +a normal command line interface and very useful for work. +:::: + +::::{group-tab} Command line +```console +$ git graph +$ git log --graph --oneline --decorate --all # if you didn't define git graph yet. +``` +:::: +::::: + + +### (5) Compare the branches + +Comparing changes is an important thing we need to do. When using the +GitHub view only, this may not be so common, but we'll show it so that +it makes sense later on. + +:::::{tabs} + +::::{group-tab} GitHub +A nice way to compare branches is to add `/compare` to the URL of the repository, +for example (replace USER): +`https://github.com/USER/planets/compare` +:::: + +::::{group-tab} VS Code +This seems to require an extension. We recommend you use the command line method. +:::: + +::::{group-tab} Command line +```console +$ git diff main new-tutorial +``` + +Try also the other way around: +```console +$ git diff new-tutorial main +``` + +Try also this if you only want to see the file names that are different: +```console +$ git diff --name-only main new-tutorial +``` +:::: +::::: + + +### (6) Compare two arbitrary commits + +This is similar to above, but not only between branches. + +:::::{tabs} +::::{group-tab} GitHub +Following the `/compare`-trick above, one can compare commits on GitHub by +adjusting the following URL: +`https://github.com/USER/planets/compare/VERSION1..VERSION2` + +Replace `USER` with your username and `VERSION1` and `VERSION2` with a commit hash or branch name. +Please try it out. +:::: + +::::{group-tab} VS Code +Again, we recommend using the Command Line method. +:::: + +::::{group-tab} Command line +First try this to get a short overview of the commits: +```console +$ git log --oneline +``` + +Then try to compare any two commit identifiers with `git diff`. +:::: +::::: + + +### (7) Renaming a branch + +:::::{tabs} +::::{group-tab} GitHub + +Branch button → View all branches → three dots at right side → Rename branch. + +:::: +::::{group-tab} VS Code +Version control sidebar → Three dots (same as in step 2) → Branch → Rename branch. Make sure you are on the right branch before you start. +:::: + +::::{group-tab} Command line +Renaming the current branch: +```console +$ git branch -m new-branch-name +``` + +Renaming a different branch: +```console +$ git branch -m different-branch new-branch-name +``` +:::: +::::: + + +### (8) Creating a tag + +Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don't move when new +commits are added. + +:::::{tabs} +::::{group-tab} GitHub +On the right side, below "Releases", click on "Create a new release". + +What GitHub calls releases are actually tags in Git with additional metadata. +For the purpose of this exercise we can use them interchangeably. +:::: + +::::{group-tab} VS Code +Version control sidebar → Three dots (same as in step 2) → Tags → Create tag. Make sure you are on the expected commit before you do this. +:::: + +::::{group-tab} Command line +Creating a tag: +```console +$ git tag -a v1.0 -m "New manuscript version for the pre-print" +``` +:::: +::::: + + +## Summary + +In this part, we saw how we can make changes to our files. With branches, we +can track several lines of work at once, and can compare their differences. + +- You could commit directly to `main` if there is only one single line + of work and it's only you. +- You could commit to branches if there are multiple lines of work at + once, and you don't want them to interfere with each other. +- Tags are useful to mark a specific commit as important, for example a + release version. +- In Git, commits form a so-called "graph". Branches are tags in Git function + like sticky notes that stick to specific commits. What this means for us is + that it does not cost any significant disk space to create new branches. +- Not all files should be added to Git. For example, temporary files or + files with sensitive information or files which are generated as part of + the build process should not be added to Git. For this we use + `.gitignore` (more about this later: {ref}`practical-advice`). +- Unsure on which branch you are or what state the repository is in? + On the command line, use `git status` frequently to get a quick overview. diff --git a/branch/main/_sources/version-control/browsing.md.txt b/branch/main/_sources/version-control/browsing.md.txt new file mode 100644 index 0000000..d3c3dc0 --- /dev/null +++ b/branch/main/_sources/version-control/browsing.md.txt @@ -0,0 +1,374 @@ +(browsing)= + +# Forking, cloning, and browsing + +In this episode, we will look at an **existing repository** to understand how +all the pieces work together. Along the way, we will make a copy (by +**forking** and/or **cloning**) of the repository for us, which will be used +for our own changes. + +:::{objectives} +- See a real Git repository and understand what is inside of it. +- Understand how version control allows advanced inspection of a + repository. +- See how Git allows multiple people to work on the same project at the same time. +- **See the big picture** instead of remembering a bunch of commands. +::: + + +## GitHub, VS Code, or command line + +We offer **three different paths** for this exercise: +- **GitHub** (this is the one we will demonstrate) +- **VS Code** (if you prefer to follow along using an editor) +- **Command line** (for people comfortable with the command line) + + +## Creating a copy of the repository by "forking" or "cloning" + +A **repository** is a collection of files in one directory tracked by Git. A +GitHub repository is GitHub's copy, which adds things like access control, +issue tracking, and discussions. Each GitHub repository is owned by a user or +organization, who controls access. + +First, we need to make **our own copy** of the exercise repository. This will +become important later, when we make our own changes. + +:::{figure} img/illustrations/fork.png +:alt: Illustration of forking a repository on GitHub +:width: 50% + +Illustration of forking a repository on GitHub. +::: + +:::{figure} img/illustrations/clone.png +:alt: Illustration of cloning a repository to your computer +:width: 50% + +Illustration of cloning a repository to your computer. +::: + +:::{figure} img/illustrations/clone-of-fork.png +:alt: Illustration of cloning a forked repository to your computer +:width: 60% + +It is also possible to do this: to clone a forked repository to your computer. +::: + +At all times you should be aware of if you are looking at **your repository** +or the **upstream repository** (original repository): +- Your repository: https://github.com/**USER**/planets +- Upstream repository: https://github.com/**workshop-material**/planets + +:::{admonition} How to create a fork +1. Go to the repository view on GitHub: +1. First, on GitHub, click the button that says "Fork". It is towards + the top-right of the screen. +1. You should shortly be redirected to your copy of the repository + **USER/planets**. +::: + +:::{instructor-note} +Before starting the exercise session show +how to fork the repository to own account +(above). +::: + + + +## Exercise: Copy and browse an existing project + +Work on this by yourself or in pairs. + + + + + + + + + + + + +::::::{prereq} Exercise preparation +:::::{tabs} +::::{group-tab} GitHub +In this case you will work on a fork. + +You only need to open your own view, as described above. The browser +URL should look like `https://github.com/USER/planets`, where +`USER` is your GitHub username. +:::: + +::::{group-tab} VS Code +You need to have forked the repository as described above. + +We need to start by making a clone of this repository so that you can work +locally. + +1. Start VS Code. +1. If you don't have the default view (you already have a project +open), go to File → New Window. +1. Under "Start" on the screen, select "Clone Git Repository...". Alternatively + navigate to the "Source Control" tab on the left sidebar and click on the "Clone Repository" button. +1. Paste in this URL: `https://github.com/USER/planets`, where + `USER` is your username. You can copy this from the browser. +1. Browse and select the folder in which you want to clone the + repository. +1. Say yes, you want to open this repository. +1. Select "Yes, I trust the authors" (the other option works too). +:::: + +::::{group-tab} Command line +**This path is advanced and we do not include all command line +information: you need to be somewhat +comfortable with the command line already.** + +You need to have forked the repository as described above. + +We need to start by making a clone of this repository so that you can work +locally. + +1. Start the terminal in which you use Git (terminal application, or + Git Bash). +1. Change to the directory where you would want the repository to be + (`cd ~/code` for example, if the `~/code` directory is where you + store your files). +1. Run the following command: `git clone + https://github.com/USER/planets`, where `USER` is your + username. You might need to use a SSH clone command instead of + HTTPS, depending on your setup. +1. Change to that directory: `cd planets` +:::: +::::: +:::::: + + +:::{exercise} Exercise: Browsing an existing project (20 min) + +Browse the [example project](https://github.com/workshop-material/planets) and +explore commits and branches, either on a fork or on a clone. Take notes and +prepare questions. The hints are for the GitHub path in the browser. + +1. Browse the **commit history**: Are commit messages understandable? + (Hint: "Commit history", the timeline symbol, above the file list) +1. Compare the commit history with the **network graph** ("Insights" -> "Network"). Can you find the branches? +1. Try to find the **history of commits for a single file**, e.g. `simulate.py`. + (Hint: "History" button in the file view) +1. **Which files include the word "position"**? + (Hint: the GitHub search on top of the repository view) +1. In the `simulate.py` file, find out **who modified each line last and when** + (click on file, then click "Blame" button). Find out who modified the gravitational constant + last and in which commit. + (Hint: "Blame" view in the file view) +1. Can you use this code yourself? **Are you allowed to share + modifications**? + (Hint: look for a license file) +::: + +The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren't enough - this is by +design. + + +## Solution and walk-through + +### (1) Basic browsing + +The most basic thing to look at is the history of commits. + +* This is visible from a button in the repository view. We see every + change, when, and who has committed. +* Every change has a unique identifier, such as `244c993`. This can + be used to identify both this change, and the whole project's + version as of that change. +* Clicking on a change in the view shows more. + +:::::{tabs} + +::::{group-tab} GitHub +Click on the timeline symbol in the repository view: + :::{figure} img/browsing/history.png + :alt: Screenshot on GitHub of where to find the commit history + :width: 100% + :class: with-border + ::: +:::: + +::::{group-tab} VS Code +This can be done from "Timeline", in the bottom of explorer, but only +for a single file. +:::: + +::::{group-tab} Command line +Run: +```console +$ git log +``` + +Try also: +```console +$ git log --oneline +``` +:::: + +::::: + + +### (2) Compare commit history with network graph + +The commit history we saw above looks linear: one commit after +another. But if we look at the network view, we see some branches and +merges. We'll see how to do these later. This is another one of the +basic Git views. + +:::::{tabs} +::::{group-tab} GitHub +In a new browser tab, open the "Insights" tab, and click on "Network". +You can hover over the commit dots to see the person who committed and +how they correspond with the commits in the other view: + :::{figure} img/browsing/network.png + :alt: Screenshot on GitHub of the network graph + :width: 100% + :class: with-border + ::: +:::: + +::::{group-tab} VS Code +We don't know how to do this without an extension. Try starting a terminal and using the +"Command line" option. +:::: + +::::{group-tab} Command line +This is a useful command to browse the network of commits locally: +```console +$ git log --graph --oneline --decorate --all +``` + +To avoid having to type this long command every time, you can define an alias (shortcut): +```console +$ git config --global alias.graph "log --graph --oneline --decorate --all" +``` + +From then on, you can use `git graph` to see the network graph. +:::: + +::::: + + +### (3) How can you browse the history of a single file? + +We see the history for the whole repository, but we can also see it +for a single file. + +:::::{tabs} + +::::{group-tab} GitHub +Navigate to the file view: Main page → simulate.py. +Click the "History" button near the top right. +:::: + +::::{group-tab} VS Code +Open simulate.py file in the editor. Under the file browser, +we see a "Timeline" view there. +:::: + +::::{group-tab} Command line +The `git log` command can take a filename and provide the log of only +a single file: + +``` +$ git log simulate.py +``` +:::: + +::::: + + +### (4) Which files include the word "position"? + +Version control makes it very easy to find all occurrences of a single +word. This is useful for things like finding where functions or +variables are defined or used. + +:::::{tabs} +::::{group-tab} GitHub +We go to the main file view. We click the Search magnifying +class at the very top, type "position", and click enter. We see every +instance, including the context. + +:::{admonition} Searching in a forked repository will not work instantaneously! + +It usually takes a few minutes before one can search for keywords in a forked repository +since it first needs to build the search index the very first time we search. +Start it, continue with other steps, then come back to this. +::: +:::: + +::::{group-tab} VS Code +If you use the "Search" magnifying class on the left sidebar, and +search for "position" it shows the occurrences in every file. You can +click to see the usage in context. +:::: + +::::{group-tab} Command line +`grep` is the command line tool that searches for lines matching a term +```console +$ git grep position # only the lines +$ git grep -C 3 position # three lines of context +$ git grep -i position # case insensitive +``` +:::: + +::::: + + +### (5) Who modified each line last and when? + +This is called the "annotate" or "blame" view. The name "blame" +is very unfortunate, but it is the standard term for historical reasons +for this functionality and it is not meant to blame anyone. + +:::::{tabs} + +::::{group-tab} GitHub +From a file view, change preview to "Blame" towards the top-left. +To get the actual commit, click on the commit message. +:::: + +::::{group-tab} VS Code +This requires an extension. We recommend for now you use the command +line version, after opening a terminal. +:::: + +::::{group-tab} Command line +These two commands are similar but have slightly different output. +```console +$ git annotate simulate.py +$ git blame simulate.py +``` +:::: + +::::: + + +### (6) Can you use this code yourself? Are you allowed to share modifications? + +- Look at the file `LICENSE`. +- On GitHub, click on the file to see a nice summary of what we can do with this: + :::{figure} img/browsing/license.png + :alt: Screenshot on GitHub summarizing license terms + :width: 100% + :class: with-border + ::: + + +### Summary + +- Git allowed us to understand this simple project much better than we + could, if it was just a few files on our own computer. +- It was easy to share the project with the course. +- By forking the repository, we created our own copy. This is + important for the following, where we will make changes to + our copy. diff --git a/branch/main/_sources/version-control/motivation.md.txt b/branch/main/_sources/version-control/motivation.md.txt new file mode 100644 index 0000000..25ab06f --- /dev/null +++ b/branch/main/_sources/version-control/motivation.md.txt @@ -0,0 +1,118 @@ +(version-control-motivation)= + +# Motivation + +:::{objectives} +- Browse **commits** and **branches** of a Git repository. +- Remember that commits are like **snapshots** of the repository at a certain + point in time. +- Know the difference between **Git** (something that tracks changes) and + **GitHub/GitLab** (a platform to host Git repositories). +::: + + +## Why do we need to keep track of versions? + +Version control is an answer to the following questions (do you recognize some +of them?): + +- "It broke ... hopefully I have a working version somewhere?" + +- "Can you please send me the latest version?" + +- "Where is the latest version?" + +- "Which version are you using?" + +- "Which version have the authors used in the paper I am trying to reproduce?" + +- "Found a bug! Since when was it there?" + +- "I am sure it used to work. When did it change?" + +- "My laptop is gone. Is my thesis now gone?" + + +## Demonstration + +- Example repository: +- Commits are like **snapshots** and if we break something we can go back to a + previous snapshot. +- Commits carry **metadata** about changes: author, date, commit message, and + a checksum. +- **Branches** are like parallel universes where you can experiment with + changes without affecting the default branch: + + ("Insights" -> "Network") +- With version control we can **annotate code** + ([example](https://github.com/workshop-material/planets/blame/main/animate.py)). +- **Collaboration**: We can fork (make a copy on GitHub), clone (make a copy + to our computer), review, compare, share, and discuss. +- **Code review**: Others can suggest changes using pull requests or merge + requests. These can be reviewed and discussed before they are merged. + Conceptually, they are similar to "suggesting changes" in Google Docs. + + +## Features: roll-back, branching, merging, collaboration + +- **Roll-back**: you can always go back to a previous version and compare + +- **Branching and merging**: + - Work on different ideas at the same time + - Different people can work on the same code/project without interfering + - You can experiment with an idea and discard it if it turns out to be a bad idea + +:::{figure} img/gophers.png +:alt: Branching explained with a gopher +:width: 100% + +Image created using +([inspiration](https://twitter.com/jay_gee/status/703360688618536960)). +::: + +- **Collaboration**: review, compare, share, discuss + +- [Example network graph](https://github.com/workshop-material/planets/network) + + +## Talking about code + +Which of these two is more practical? +1. "Clone the code, go to the file 'simulate.py', and search for 'force_between_planets'. + Oh! But make sure you use the version from September 2024." +1. Or I can send you a permalink: + + +## What we typically like to snapshot + +- Software (this is how it started but Git/GitHub can track a lot more) +- Scripts +- Documents (plain text files much better suitable than Word documents) +- Manuscripts (Git is great for collaborating/sharing LaTeX or [Quarto](https://quarto.org/) manuscripts) +- Configuration files +- Website sources +- Data + +::::{discussion} + In this example somebody tried to keep track of versions without a version + control system tool like Git. Discuss the following directory listing. What + possible problems do you anticipate with this kind of "version control": + ```text + myproject-2019.zip + myproject-2020-february.zip + myproject-2021-august.zip + myproject-2023-09-19-working.zip + myproject-2023-09-21.zip + myproject-2023-09-21-test.zip + myproject-2023-09-21-myversion.zip + myproject-2023-09-21-newfeature.zip + ... + (100 more files like these) + ``` + + :::{solution} + - Giving a version to a collaborator and merging changes later with own + changes sounds like lots of work. + - What if you discover a bug and want to know since when the bug existed? + ::: +:::: diff --git a/branch/main/_sources/version-control/practical-advice.md.txt b/branch/main/_sources/version-control/practical-advice.md.txt new file mode 100644 index 0000000..5e4c460 --- /dev/null +++ b/branch/main/_sources/version-control/practical-advice.md.txt @@ -0,0 +1,136 @@ +(practical-advice)= + +# Practical advice: How much Git is necessary? + + +## Writing useful commit messages + +Useful commit messages **summarize the change and provide context**. + +If you need a commit message that is longer than one line, +then the convention is: **one line summarizing the commit, then one empty line, +then paragraph(s) with more details in free form, if necessary**. + +Good example: +```{code-block} text +--- +emphasize-lines: 1 +--- +increase alpha to 2.0 for faster convergence + +the motivation for this change is +to enable ... +... +(more context) +... +this is based on a discussion in #123 +``` + +- **Why something was changed is more important than what has changed.** +- Cross-reference to issues and discussions if possible/relevant. +- Bad commit messages: "fix", "oops", "save work" +- Bad examples: [http://whatthecommit.com](http://whatthecommit.com) +- Write commit messages that will be understood + 15 years from now by someone else than you. Or by your future you. +- Many projects start out as projects "just for me" and end up to be successful projects + that are developed by 50 people over decades. +- [Commits with multiple authors](https://help.github.com/articles/creating-a-commit-with-multiple-authors/) are possible. + +Good references: + +- ["My favourite Git commit"](https://fatbusinessman.com/2019/my-favourite-git-commit) +- ["On commit messages"](https://who-t.blogspot.com/2009/12/on-commit-messages.html) +- ["How to Write a Git Commit Message"](https://chris.beams.io/posts/git-commit/) + +```{note} +A great way to learn how to write commit messages and to get inspired by their +style choices: **browse repositories of codes that you use/like**: + +Some examples (but there are so many good examples): +- [SciPy](https://github.com/scipy/scipy/commits/main) +- [NumPy](https://github.com/numpy/numpy/commits/main) +- [Pandas](https://github.com/pandas-dev/pandas/commits/main) +- [Julia](https://github.com/JuliaLang/julia/commits/master) +- [ggplot2](https://github.com/tidyverse/ggplot2/commits/main), + compare with their [release + notes](https://github.com/tidyverse/ggplot2/releases) +- [Flask](https://github.com/pallets/flask/commits/main), + compare with their [release + notes](https://github.com/pallets/flask/blob/main/CHANGES.rst) + +When designing commit message styles consider also these: +- How will you easily generate a changelog or release notes? +- During code review, you can help each other improving commit messages. +``` + +But remember: it is better to make any commit, than no commit. Especially in small projects. +**Let not the perfect be the enemy of the good enough**. + + +## What level of branching complexity is necessary for each project? + +**Simple personal projects**: +- Typically start with just the `main` branch. +- Use branches for unfinished/untested ideas. +- Use branches when you are not sure about a change. +- Use tags to mark important milestones. +- If you are unsure what to do with unfinished and not working code, commit it + to a branch. + +**Projects with few persons: you accept things breaking sometimes** +- It might be reasonable to commit to the `main` branch and feature branches. + +**Projects with few persons: changes are reviewed by others** +- You create new feature branches for changes. +- Changes are reviewed before they are merged to the `main` branch. +- Consider to write-protect the `main` branch so that it can only be changed + with pull requests or merge requests. + + +## How large should a commit be? + +- Better too small than too large (easier to combine than to split). +- Often I make a commit at the end of the day (this is a unit I would not like to lose). +- Smaller sized commits may be easier to review for others than huge commits. +- Imperfect commits are better than no commits. +- A commit should not contain unrelated changes to simplify review and possible + repair/adjustments/undo later (but again: imperfect commits are better than no commits). + + +## Working on the command line? Use "git status" all the time + +The `git status` command is one of the most useful commands in Git +to inform about which branch we are on, what we are about to commit, +which files might not be tracked, etc. + + +## How about staging and committing? + +- Commit early and often: rather create too many commits than too few. + You can always combine commits later. +- Once you commit, it is very, very hard to really lose your code. +- Always fully commit (or stash) before you do dangerous things, so that you know you are safe. + Otherwise it can be hard to recover. +- Later you can start using the staging area (where you first stage and then commit in a second step). +- Later start using `git add -p` and/or `git commit -p`. + + +## What to avoid + +- Committing **generated files/directories** (example: `__pycache__`, `*.pyc`) -> + use [.gitignore + files](https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files) + ([collection of .gitignore templates](https://github.com/github/gitignore)). + +- Committing **huge files** -> use code review to detect this. + +- Committing **unrelated changes** together. + +- Postponing commits because the changes are "unfinished"/"ugly" -> better ugly + commits than no commits. + +- When working with branches: + - Not updating your branch before starting new work. + - Too ambitious branch which risks to never get completed. + - Over-engineering the branch layout and safeguards in small projects. + - Working on unrelated things on the same branch. diff --git a/branch/main/_sources/version-control/sharing.md.txt b/branch/main/_sources/version-control/sharing.md.txt new file mode 100644 index 0000000..6fb6401 --- /dev/null +++ b/branch/main/_sources/version-control/sharing.md.txt @@ -0,0 +1,301 @@ +(sharing-repositories)= + +# Optional: How to turn your project to a Git repo and share it + +:::{objectives} +- Turn our own coding project (small or large, finished or unfinished) into a + Git repository. +- Be able to share a repository on the web to have a backup or so that others + can reuse and collaborate or even just find it. +::: + + +## Exercise + +:::{figure} img/illustrations/sharing.png +:alt: Illustration of how we turn a project into a Git repository and share it +:width: 100% + +From a bunch of files to a local repository which we then share on GitHub. +::: + + +:::::{exercise} Exercise: Turn your project to a Git repo and share it (20 min) + +1. Create a new directory called **myproject** with one or few files in it. + This represents our own project. It is not yet a Git repository. You can try + that with your own project or use a simple placeholder example. +2. Turn this new directory into a Git repository. +3. Share this repository on GitHub (or GitLab, since it really works the same). + +We offer **three different paths** of how to do this exercise. +* Via **GitHub web interface**: easy and can be a good starting point if you are completely + new to Git. +* **VS Code** is quite easy, since VS Code can offer to create the + GitHub repositories for you. +* **Command line**: you need to create the + repository on GitHub and link it yourself. + +::::{tabs} + +:::{group-tab} Only using GitHub +### Create an repository on GitHub + +First log into GitHub, then follow the screenshots and descriptions below. +```{figure} img/sharing/new-repository.png +:alt: Screenshot on GitHub before a new repository form is opened +:width: 60% +:class: with-border + +Click on the "plus" symbol on top right, then on "New repository". +``` + +Then: +```{figure} img/sharing/create-repository-with-readme.png +:alt: Screenshot on GitHub just before a new repository is created +:width: 100% +:class: with-border + +Choose a repository name, add a short description, and in this case **make sure to check** "Add a +README file". Finally "Create repository". +``` + + +### Upload your files + +Now that the repository is created, you can upload your files: +```{figure} img/sharing/upload-files.png +:alt: Screenshot on GitHub just before uploading files +:width: 100% +:class: with-border + +Click on the "+" symbol and then on "Upload files". +``` +::: + +:::{group-tab} VS Code +In VS Code it only takes few clicks. + +First, open the folder in VS Code. My example project here contains two files. +Then click on the source control icon: +```{figure} img/sharing/vscode-start.png +:alt: Screenshot of VS Code before clicking on the source control icon +:width: 100% +:class: with-border + +Open the folder in VS Code. Then click on the source control icon. +``` + +```{figure} img/sharing/vscode-publish-to-github1.png +:alt: Screenshot of VS Code before publishing to GitHub +:width: 100% +:class: with-border + +Choose "Publish to GitHub". In this case I never even clicked on "Initialize Repository". +``` + +```{figure} img/sharing/vscode-publish-to-github2.png +:alt: Screenshot of VS Code before publishing to GitHub +:width: 100% +:class: with-border + +In my case I chose to "Publish to GitHub public repository". Here you can also rename +the repository if needed. +``` + +```{figure} img/sharing/vscode-authorize.png +:alt: Screenshot of VS Code asking for authorization +:width: 50% +:class: with-border + +First time you do this you might need to authorize VS Code to access your +GitHub account by redirecting you to https://github.com/login/oauth/authorize. +``` + +```{figure} img/sharing/vscode-publish-to-github3.png +:alt: Screenshot of VS Code after publishing to GitHub +:width: 100% +:class: with-border + +After it is published, click on "Open on GitHub". +``` +::: + +:::{group-tab} Command line +### Put your project under version control + +My example project here consists of two files. Replace this with your own +example files: +```console +$ ls -l + +.rw------- 19k user 7 Mar 17:36 LICENSE +.rw------- 21 user 7 Mar 17:36 myscript.py +``` + +I will first initialize a Git repository in this directory. +If you get an error, try without the `-b main` (and your default branch will +then be called `master`, this will happen for Git versions older than +2.28): +```console +$ git init -b main +``` + +Now add and commit the two files to the Git repository: +```console +$ git add LICENSE myscript.py +$ git commit -m "putting my project under version control" +``` + +If you want to add all files in one go, you can use `git +add .` instead of `git add LICENSE myscript.py`. + +Now you have a Git repository with one commit. Verify this with `git log`. +But it's still only on your computer. Let's put it on GitHub next. + + +### Create an empty repository on GitHub + +First log into GitHub, then follow the screenshots and descriptions below. +```{figure} img/sharing/new-repository.png +:alt: Screenshot on GitHub before a new repository form is opened +:width: 60% +:class: with-border + +Click on the "plus" symbol on top right, then on "New repository". +``` + +Then create an empty repository without any files and without any commits: +```{figure} img/sharing/create-repository.png +:alt: Screenshot on GitHub just before a new repository is created +:width: 100% +:class: with-border + +Choose a repository name, add a short description, but please **do not check** "Add a +README file". For "Add .gitignore" and "Choose a license" also leave as "None". Finally "Create repository". +``` + +Once you click the green "Create repository", you will see a page similar to: +```{figure} img/sharing/bare-repository.png +:alt: Screenshot on GitHub after a bare repository was created +:width: 100% +:class: with-border +``` + +What this means is that we have now an empty project with either an HTTPS or an +SSH address: click on the HTTPS and SSH buttons to see what happens. + + +### Push an existing repository from your computer to GitHub + +We now want to follow the "**... or push an existing repository from the command line**": + +1. In your terminal make sure you are still in your myproject directory. +2. Copy paste the three lines below the red arrow to the terminal and execute + those, in my case (**you need to replace the "USER" part and possibly also + the repository name**): + +`````{tabs} + ````{group-tab} SSH + ```console + $ git remote add origin git@github.com:USER/myproject.git + ``` + ```` + ````{group-tab} HTTPS + ```console + $ git remote add origin https://github.com/USER/myproject.git + ``` + ```` +````` + +Then: +```console +$ git branch -M main +$ git push -u origin main +``` + +The meaning of the above lines: +- Add a remote reference with the name "origin" +- Rename current branch to "main" +- Push branch "main" to "origin" + +You should now see: + +```text +Enumerating objects: 4, done. +Counting objects: 100% (4/4), done. +Delta compression using up to 12 threads +Compressing objects: 100% (3/3), done. +Writing objects: 100% (4/4), 6.08 KiB | 6.08 MiB/s, done. +Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 +To github.com:USER/myproject.git + * [new branch] main -> main +branch 'main' set up to track 'origin/main'. +``` + +Reload your GitHub project website and your commits should now be +online! + + +### Troubleshooting + +**error: remote origin already exists** +- Explanation: You probably ran a `git remote add origin ...` command, then changed your + mind about HTTPS or SSH and then tried to run the other `git remote add + origin ...` command but "origin" then already exists. +- Recovery: + - First remove "origin" with `git remote remove origin` + - Then run the correct `git remote add origin ...` command + +**remote contains work that you do not have** +- Explanation: You probably clicked on "Add a README file" and now the + repository on GitHub is not empty but contains one commit and locally you + have a different history. Git now prevents you from accidentally overwriting + the history on GitHub. +- Recovery: + - Use `git push --force` instead of `git push`, which will force Git to overwrite the history on GitHub + - Note that this is a powerful but also possibly dangerous option but here it + helps us. If it's a brand new repo, it probably is fine to do this. For real + repositories, don't do this unless you are very sure what is happening. +::: + +:::{group-tab} RStudio + +This is not fully explained, because a lot of it is similar to the "Command +line" method (and an RStudio expert could help us some). The main differences +are: + +### Put your project under version control + +1. Tools → Version control → Project setup → Version control system = Git. +1. Select "Yes" for "do you want to initialize a new git repository for this project. +1. Select yes to restart the project with RStudio. +1. Switch to branch `main` to have you branch named that. + +### Create an empty repository on GitHub + +Same as command line + +### Push an existing repository from your computer to GitHub + +1. Under the "Create new branch" button → "Add Remote" +1. Remote name: `origin` +1. Remote URL: as in command line (remember to select SSH or HTTPS as you have configured your RStudio) +1. The "Push" (up arrow) button will send changes on your current branch to the remote. The "Pull" (down arrow) will get changes from the remote. + +### Troubleshooting + +Same as command line + +::: +:::: +::::: + + +## Is putting software on GitHub/GitLab/... publishing? + +It is a good first step but to make your code truly **findable and +accessible**, consider making your code **citable and persistent**: Get a +persistent identifier (PID) such as DOI in addition to sharing the code +publicly, by using services like [Zenodo](https://zenodo.org) or similar +services. diff --git a/branch/main/_static/_sphinx_javascript_frameworks_compat.js b/branch/main/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000..8141580 --- /dev/null +++ b/branch/main/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/branch/main/_static/basic.css b/branch/main/_static/basic.css new file mode 100644 index 0000000..f316efc --- /dev/null +++ b/branch/main/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/branch/main/_static/check-solid.svg b/branch/main/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/branch/main/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/branch/main/_static/clipboard.min.js b/branch/main/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/branch/main/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/branch/main/_static/copybutton.css b/branch/main/_static/copybutton.css new file mode 100644 index 0000000..40eafe5 --- /dev/null +++ b/branch/main/_static/copybutton.css @@ -0,0 +1,93 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +.highlight:hover button.copybtn { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/branch/main/_static/copybutton.js b/branch/main/_static/copybutton.js new file mode 100644 index 0000000..f3ecd03 --- /dev/null +++ b/branch/main/_static/copybutton.js @@ -0,0 +1,241 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copié dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for two seconds, then changes it back +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000) + setTimeout(() => el.classList.remove('success'), 2000) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, 2000) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos, .gp'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/branch/main/_static/copybutton_funcs.js b/branch/main/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/branch/main/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/branch/main/_static/css/badge_only.css b/branch/main/_static/css/badge_only.css new file mode 100644 index 0000000..c718cee --- /dev/null +++ b/branch/main/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/branch/main/_static/css/fonts/Roboto-Slab-Bold.woff b/branch/main/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/branch/main/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/branch/main/_static/css/fonts/Roboto-Slab-Bold.woff2 b/branch/main/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/branch/main/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/branch/main/_static/css/fonts/Roboto-Slab-Regular.woff b/branch/main/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/branch/main/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/branch/main/_static/css/fonts/Roboto-Slab-Regular.woff2 b/branch/main/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/branch/main/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/branch/main/_static/css/fonts/fontawesome-webfont.eot b/branch/main/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/branch/main/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/branch/main/_static/css/fonts/fontawesome-webfont.svg b/branch/main/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/branch/main/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/branch/main/_static/css/fonts/fontawesome-webfont.ttf b/branch/main/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/branch/main/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/branch/main/_static/css/fonts/fontawesome-webfont.woff b/branch/main/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/branch/main/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/branch/main/_static/css/fonts/fontawesome-webfont.woff2 b/branch/main/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/branch/main/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/branch/main/_static/css/fonts/lato-bold-italic.woff b/branch/main/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/branch/main/_static/css/fonts/lato-bold-italic.woff differ diff --git a/branch/main/_static/css/fonts/lato-bold-italic.woff2 b/branch/main/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/branch/main/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/branch/main/_static/css/fonts/lato-bold.woff b/branch/main/_static/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/branch/main/_static/css/fonts/lato-bold.woff differ diff --git a/branch/main/_static/css/fonts/lato-bold.woff2 b/branch/main/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/branch/main/_static/css/fonts/lato-bold.woff2 differ diff --git a/branch/main/_static/css/fonts/lato-normal-italic.woff b/branch/main/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/branch/main/_static/css/fonts/lato-normal-italic.woff differ diff --git a/branch/main/_static/css/fonts/lato-normal-italic.woff2 b/branch/main/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/branch/main/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/branch/main/_static/css/fonts/lato-normal.woff b/branch/main/_static/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/branch/main/_static/css/fonts/lato-normal.woff differ diff --git a/branch/main/_static/css/fonts/lato-normal.woff2 b/branch/main/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/branch/main/_static/css/fonts/lato-normal.woff2 differ diff --git a/branch/main/_static/css/theme.css b/branch/main/_static/css/theme.css new file mode 100644 index 0000000..19a446a --- /dev/null +++ b/branch/main/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/branch/main/_static/doctools.js b/branch/main/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/branch/main/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/branch/main/_static/documentation_options.js b/branch/main/_static/documentation_options.js new file mode 100644 index 0000000..87fc516 --- /dev/null +++ b/branch/main/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'dirhtml', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/branch/main/_static/file.png b/branch/main/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/branch/main/_static/file.png differ diff --git a/branch/main/_static/jquery.js b/branch/main/_static/jquery.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/branch/main/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/branch/main/_static/js/html5shiv.min.js b/branch/main/_static/js/html5shiv.min.js new file mode 100644 index 0000000..cd1c674 --- /dev/null +++ b/branch/main/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/branch/main/_static/js/theme.js b/branch/main/_static/js/theme.js new file mode 100644 index 0000000..1fddb6e --- /dev/null +++ b/branch/main/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/branch/main/_static/minipres.js b/branch/main/_static/minipres.js new file mode 100644 index 0000000..ad11c87 --- /dev/null +++ b/branch/main/_static/minipres.js @@ -0,0 +1,223 @@ +// Add goTo method to elements +// http://stackoverflow.com/questions/4801655/how-to-go-to-a-specific-element-on-page +(function($) { + $.fn.goTo = function() { + $('html, body').animate({ + scrollTop: $(this).offset().top //+ 'px' + }, 'fast'); + return this; // for chaining... + } +})(jQuery); + +// NO good way to do this!. Copy a hack from here +// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript +// https://stackoverflow.com/a/2880929 +var urlParams; +(window.onpopstate = function () { + var match, + pl = /\+/g, // Regex for replacing addition symbol with a space + search = /([^&=]+)=?([^&]*)/g, + decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, + query = window.location.search.substring(1); + urlParams = {}; + while (match = search.exec(query)) + urlParams[decode(match[1])] = decode(match[2]); +})(); + +// Select heading levels +var maxHeading = urlParams['h'] +if (maxHeading === undefined) maxHeading = 2 +var headingLevels = []; +for (h=2 ; h (sections.length-1) ) { + // if we would scroll past bottom, or above top, do nothing + return; + } + + console.log('xxxxxx'); + var targetSection = sections[targetPos]; + console.log(targetSection, typeof(targetSection)); + + // Return targetSection top and height + var secProperties = section_top_and_height(targetSection); + var top = secProperties['top']; + var height = secProperties['height'] + var win_height = window.innerHeight; + //console.info(top, height, win_height) + + var scroll_to = 0; + if (height >= win_height || height == 0) { + scroll_to = top; + } else { + scroll_to = top - (win_height-height)/3.; + } + //console.info(top, height, win_height, scroll_to) + + $('html, body').animate({ + scrollTop: scroll_to //+ 'px' + }, 'fast'); + +} + + +function minipres() { + /* Enable the minipres mode: + - call the hide() function + - set up the scrolling listener + */ + document.addEventListener('keydown', function (event) { + switch(event.which) { + case 37: // left + switch_slide(-1); + event.preventDefault(); + return false; + break; + //case 38: // up + case 39: // right + switch_slide(+1); + event.preventDefault(); + return false; + break; + //case 40: // down + default: + return; // exit this handler for other keys + } + }, true) + + hide() + + // Increase space between sections + //$("div .section").css('margin-bottom', '50%'); + $(sectionSelector).css('margin-top', '50%'); + + // Reduce size/color of other sections + if (hiddenSectionSelector.length > 0) { + var hideNodes = $(hiddenSectionSelector); + console.log(typeof hideNodes, hideNodes); + for (node in hideNodes) { + console.log("a", typeof node, node); + node = hideNodes[node]; // what's right way to iterate values? + console.log("b", typeof node, node); + if (node.parentNode && node.parentNode.className == "section") { + node = node.parentNode; + console.log("c", typeof node, node); + //node.css['transform'] = 'scale(.5)'; + //node.css['transform-origin'] = 'top center'; + $(node).css('color', 'lightgrey'); + //$(node).css('font-size', '20%'); + //$(node).css('visibility', 'collapse'); + //ntahousnatouhasno; + } + } + } +} + +function hide() { + /* Hide all non-essential elements on the page + */ + + // This is for sphinx_rst_theme and readthedocs + $(".wy-nav-side").remove(); + $(".wy-nav-content-wrap").css('margin-left', 0); + $('.rst-versions').remove(); // readthedocs version selector + + // Add other formats here. +} + + +var slideshow = minipres; + +if (window.location.search.match(/[?&](minipres|slideshow|pres)([=&]|$)/) ) { + //minipres() + window.addEventListener("load", minipres); +} else if (window.location.search.match(/[?&](plain)([=&]|$)/) ) { + window.addEventListener("load", hide); +} diff --git a/branch/main/_static/minus.png b/branch/main/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/branch/main/_static/minus.png differ diff --git a/branch/main/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css b/branch/main/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css new file mode 100644 index 0000000..3356631 --- /dev/null +++ b/branch/main/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css @@ -0,0 +1,2342 @@ +/* Variables */ +:root { + --mystnb-source-bg-color: #f7f7f7; + --mystnb-stdout-bg-color: #fcfcfc; + --mystnb-stderr-bg-color: #fdd; + --mystnb-traceback-bg-color: #fcfcfc; + --mystnb-source-border-color: #ccc; + --mystnb-source-margin-color: green; + --mystnb-stdout-border-color: #f7f7f7; + --mystnb-stderr-border-color: #f7f7f7; + --mystnb-traceback-border-color: #ffd6d6; + --mystnb-hide-prompt-opacity: 70%; + --mystnb-source-border-radius: .4em; + --mystnb-source-border-width: 1px; +} + +/* Whole cell */ +div.container.cell { + padding-left: 0; + margin-bottom: 1em; +} + +/* Removing all background formatting so we can control at the div level */ +.cell_input div.highlight, +.cell_output pre, +.cell_input pre, +.cell_output .output { + border: none; + box-shadow: none; +} + +.cell_output .output pre, +.cell_input pre { + margin: 0px; +} + +/* Input cells */ +div.cell div.cell_input, +div.cell details.above-input>summary { + padding-left: 0em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + background-color: var(--mystnb-source-bg-color); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; + border-radius: var(--mystnb-source-border-radius); +} + +div.cell_input>div, +div.cell_output div.output>div.highlight { + margin: 0em !important; + border: none !important; +} + +/* All cell outputs */ +.cell_output { + padding-left: 1em; + padding-right: 0em; + margin-top: 1em; +} + +/* Text outputs from cells */ +.cell_output .output.text_plain, +.cell_output .output.traceback, +.cell_output .output.stream, +.cell_output .output.stderr { + margin-top: 1em; + margin-bottom: 0em; + box-shadow: none; +} + +.cell_output .output.text_plain, +.cell_output .output.stream { + background: var(--mystnb-stdout-bg-color); + border: 1px solid var(--mystnb-stdout-border-color); +} + +.cell_output .output.stderr { + background: var(--mystnb-stderr-bg-color); + border: 1px solid var(--mystnb-stderr-border-color); +} + +.cell_output .output.traceback { + background: var(--mystnb-traceback-bg-color); + border: 1px solid var(--mystnb-traceback-border-color); +} + +/* Collapsible cell content */ +div.cell details.above-input div.cell_input { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; +} + +div.cell div.cell_input.above-output-prompt { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +div.cell details.above-input>summary { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; + padding-left: 1em; + margin-bottom: 0; +} + +div.cell details.above-output>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.below-input>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-top: none; + border-bottom-left-radius: var(--mystnb-source-border-radius); + border-bottom-right-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.hide>summary>span { + opacity: var(--mystnb-hide-prompt-opacity); +} + +div.cell details.hide[open]>summary>span.collapsed { + display: none; +} + +div.cell details.hide:not([open])>summary>span.expanded { + display: none; +} + +@keyframes collapsed-fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} +div.cell details.hide[open]>summary~* { + -moz-animation: collapsed-fade-in 0.3s ease-in-out; + -webkit-animation: collapsed-fade-in 0.3s ease-in-out; + animation: collapsed-fade-in 0.3s ease-in-out; +} + +/* Math align to the left */ +.cell_output .MathJax_Display { + text-align: left !important; +} + +/* Pandas tables. Pulled from the Jupyter / nbsphinx CSS */ +div.cell_output table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 1em; + table-layout: fixed; +} + +div.cell_output thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} + +div.cell_output tr, +div.cell_output th, +div.cell_output td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} + +div.cell_output th { + font-weight: bold; +} + +div.cell_output tbody tr:nth-child(odd) { + background: #f5f5f5; +} + +div.cell_output tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + +/** source code line numbers **/ +span.linenos { + opacity: 0.5; +} + +/* Inline text from `paste` operation */ + +span.pasted-text { + font-weight: bold; +} + +span.pasted-inline img { + max-height: 2em; +} + +tbody span.pasted-inline img { + max-height: none; +} + +/* Font colors for translated ANSI escape sequences +Color values are copied from Jupyter Notebook +https://github.com/jupyter/notebook/blob/52581f8eda9b319eb0390ac77fe5903c38f81e3e/notebook/static/notebook/less/ansicolors.less#L14-L21 +Background colors from +https://nbsphinx.readthedocs.io/en/latest/code-cells.html#ANSI-Colors +*/ +div.highlight .-Color-Bold { + font-weight: bold; +} + +div.highlight .-Color[class*=-Black] { + color: #3E424D +} + +div.highlight .-Color[class*=-Red] { + color: #E75C58 +} + +div.highlight .-Color[class*=-Green] { + color: #00A250 +} + +div.highlight .-Color[class*=-Yellow] { + color: #DDB62B +} + +div.highlight .-Color[class*=-Blue] { + color: #208FFB +} + +div.highlight .-Color[class*=-Magenta] { + color: #D160C4 +} + +div.highlight .-Color[class*=-Cyan] { + color: #60C6C8 +} + +div.highlight .-Color[class*=-White] { + color: #C5C1B4 +} + +div.highlight .-Color[class*=-BGBlack] { + background-color: #3E424D +} + +div.highlight .-Color[class*=-BGRed] { + background-color: #E75C58 +} + +div.highlight .-Color[class*=-BGGreen] { + background-color: #00A250 +} + +div.highlight .-Color[class*=-BGYellow] { + background-color: #DDB62B +} + +div.highlight .-Color[class*=-BGBlue] { + background-color: #208FFB +} + +div.highlight .-Color[class*=-BGMagenta] { + background-color: #D160C4 +} + +div.highlight .-Color[class*=-BGCyan] { + background-color: #60C6C8 +} + +div.highlight .-Color[class*=-BGWhite] { + background-color: #C5C1B4 +} + +/* Font colors for 8-bit ANSI */ + +div.highlight .-Color[class*=-C0] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC0] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C1] { + color: #800000 +} + +div.highlight .-Color[class*=-BGC1] { + background-color: #800000 +} + +div.highlight .-Color[class*=-C2] { + color: #008000 +} + +div.highlight .-Color[class*=-BGC2] { + background-color: #008000 +} + +div.highlight .-Color[class*=-C3] { + color: #808000 +} + +div.highlight .-Color[class*=-BGC3] { + background-color: #808000 +} + +div.highlight .-Color[class*=-C4] { + color: #000080 +} + +div.highlight .-Color[class*=-BGC4] { + background-color: #000080 +} + +div.highlight .-Color[class*=-C5] { + color: #800080 +} + +div.highlight .-Color[class*=-BGC5] { + background-color: #800080 +} + +div.highlight .-Color[class*=-C6] { + color: #008080 +} + +div.highlight .-Color[class*=-BGC6] { + background-color: #008080 +} + +div.highlight .-Color[class*=-C7] { + color: #C0C0C0 +} + +div.highlight .-Color[class*=-BGC7] { + background-color: #C0C0C0 +} + +div.highlight .-Color[class*=-C8] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC8] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C9] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC9] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C10] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC10] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C11] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC11] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C12] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC12] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C13] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC13] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C14] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC14] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C15] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC15] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C16] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC16] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C17] { + color: #00005F +} + +div.highlight .-Color[class*=-BGC17] { + background-color: #00005F +} + +div.highlight .-Color[class*=-C18] { + color: #000087 +} + +div.highlight .-Color[class*=-BGC18] { + background-color: #000087 +} + +div.highlight .-Color[class*=-C19] { + color: #0000AF +} + +div.highlight .-Color[class*=-BGC19] { + background-color: #0000AF +} + +div.highlight .-Color[class*=-C20] { + color: #0000D7 +} + +div.highlight .-Color[class*=-BGC20] { + background-color: #0000D7 +} + +div.highlight .-Color[class*=-C21] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC21] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C22] { + color: #005F00 +} + +div.highlight .-Color[class*=-BGC22] { + background-color: #005F00 +} + +div.highlight .-Color[class*=-C23] { + color: #005F5F +} + +div.highlight .-Color[class*=-BGC23] { + background-color: #005F5F +} + +div.highlight .-Color[class*=-C24] { + color: #005F87 +} + +div.highlight .-Color[class*=-BGC24] { + background-color: #005F87 +} + +div.highlight .-Color[class*=-C25] { + color: #005FAF +} + +div.highlight .-Color[class*=-BGC25] { + background-color: #005FAF +} + +div.highlight .-Color[class*=-C26] { + color: #005FD7 +} + +div.highlight .-Color[class*=-BGC26] { + background-color: #005FD7 +} + +div.highlight .-Color[class*=-C27] { + color: #005FFF +} + +div.highlight .-Color[class*=-BGC27] { + background-color: #005FFF +} + +div.highlight .-Color[class*=-C28] { + color: #008700 +} + +div.highlight .-Color[class*=-BGC28] { + background-color: #008700 +} + +div.highlight .-Color[class*=-C29] { + color: #00875F +} + +div.highlight .-Color[class*=-BGC29] { + background-color: #00875F +} + +div.highlight .-Color[class*=-C30] { + color: #008787 +} + +div.highlight .-Color[class*=-BGC30] { + background-color: #008787 +} + +div.highlight .-Color[class*=-C31] { + color: #0087AF +} + +div.highlight .-Color[class*=-BGC31] { + background-color: #0087AF +} + +div.highlight .-Color[class*=-C32] { + color: #0087D7 +} + +div.highlight .-Color[class*=-BGC32] { + background-color: #0087D7 +} + +div.highlight .-Color[class*=-C33] { + color: #0087FF +} + +div.highlight .-Color[class*=-BGC33] { + background-color: #0087FF +} + +div.highlight .-Color[class*=-C34] { + color: #00AF00 +} + +div.highlight .-Color[class*=-BGC34] { + background-color: #00AF00 +} + +div.highlight .-Color[class*=-C35] { + color: #00AF5F +} + +div.highlight .-Color[class*=-BGC35] { + background-color: #00AF5F +} + +div.highlight .-Color[class*=-C36] { + color: #00AF87 +} + +div.highlight .-Color[class*=-BGC36] { + background-color: #00AF87 +} + +div.highlight .-Color[class*=-C37] { + color: #00AFAF +} + +div.highlight .-Color[class*=-BGC37] { + background-color: #00AFAF +} + +div.highlight .-Color[class*=-C38] { + color: #00AFD7 +} + +div.highlight .-Color[class*=-BGC38] { + background-color: #00AFD7 +} + +div.highlight .-Color[class*=-C39] { + color: #00AFFF +} + +div.highlight .-Color[class*=-BGC39] { + background-color: #00AFFF +} + +div.highlight .-Color[class*=-C40] { + color: #00D700 +} + +div.highlight .-Color[class*=-BGC40] { + background-color: #00D700 +} + +div.highlight .-Color[class*=-C41] { + color: #00D75F +} + +div.highlight .-Color[class*=-BGC41] { + background-color: #00D75F +} + +div.highlight .-Color[class*=-C42] { + color: #00D787 +} + +div.highlight .-Color[class*=-BGC42] { + background-color: #00D787 +} + +div.highlight .-Color[class*=-C43] { + color: #00D7AF +} + +div.highlight .-Color[class*=-BGC43] { + background-color: #00D7AF +} + +div.highlight .-Color[class*=-C44] { + color: #00D7D7 +} + +div.highlight .-Color[class*=-BGC44] { + background-color: #00D7D7 +} + +div.highlight .-Color[class*=-C45] { + color: #00D7FF +} + +div.highlight .-Color[class*=-BGC45] { + background-color: #00D7FF +} + +div.highlight .-Color[class*=-C46] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC46] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C47] { + color: #00FF5F +} + +div.highlight .-Color[class*=-BGC47] { + background-color: #00FF5F +} + +div.highlight .-Color[class*=-C48] { + color: #00FF87 +} + +div.highlight .-Color[class*=-BGC48] { + background-color: #00FF87 +} + +div.highlight .-Color[class*=-C49] { + color: #00FFAF +} + +div.highlight .-Color[class*=-BGC49] { + background-color: #00FFAF +} + +div.highlight .-Color[class*=-C50] { + color: #00FFD7 +} + +div.highlight .-Color[class*=-BGC50] { + background-color: #00FFD7 +} + +div.highlight .-Color[class*=-C51] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC51] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C52] { + color: #5F0000 +} + +div.highlight .-Color[class*=-BGC52] { + background-color: #5F0000 +} + +div.highlight .-Color[class*=-C53] { + color: #5F005F +} + +div.highlight .-Color[class*=-BGC53] { + background-color: #5F005F +} + +div.highlight .-Color[class*=-C54] { + color: #5F0087 +} + +div.highlight .-Color[class*=-BGC54] { + background-color: #5F0087 +} + +div.highlight .-Color[class*=-C55] { + color: #5F00AF +} + +div.highlight .-Color[class*=-BGC55] { + background-color: #5F00AF +} + +div.highlight .-Color[class*=-C56] { + color: #5F00D7 +} + +div.highlight .-Color[class*=-BGC56] { + background-color: #5F00D7 +} + +div.highlight .-Color[class*=-C57] { + color: #5F00FF +} + +div.highlight .-Color[class*=-BGC57] { + background-color: #5F00FF +} + +div.highlight .-Color[class*=-C58] { + color: #5F5F00 +} + +div.highlight .-Color[class*=-BGC58] { + background-color: #5F5F00 +} + +div.highlight .-Color[class*=-C59] { + color: #5F5F5F +} + +div.highlight .-Color[class*=-BGC59] { + background-color: #5F5F5F +} + +div.highlight .-Color[class*=-C60] { + color: #5F5F87 +} + +div.highlight .-Color[class*=-BGC60] { + background-color: #5F5F87 +} + +div.highlight .-Color[class*=-C61] { + color: #5F5FAF +} + +div.highlight .-Color[class*=-BGC61] { + background-color: #5F5FAF +} + +div.highlight .-Color[class*=-C62] { + color: #5F5FD7 +} + +div.highlight .-Color[class*=-BGC62] { + background-color: #5F5FD7 +} + +div.highlight .-Color[class*=-C63] { + color: #5F5FFF +} + +div.highlight .-Color[class*=-BGC63] { + background-color: #5F5FFF +} + +div.highlight .-Color[class*=-C64] { + color: #5F8700 +} + +div.highlight .-Color[class*=-BGC64] { + background-color: #5F8700 +} + +div.highlight .-Color[class*=-C65] { + color: #5F875F +} + +div.highlight .-Color[class*=-BGC65] { + background-color: #5F875F +} + +div.highlight .-Color[class*=-C66] { + color: #5F8787 +} + +div.highlight .-Color[class*=-BGC66] { + background-color: #5F8787 +} + +div.highlight .-Color[class*=-C67] { + color: #5F87AF +} + +div.highlight .-Color[class*=-BGC67] { + background-color: #5F87AF +} + +div.highlight .-Color[class*=-C68] { + color: #5F87D7 +} + +div.highlight .-Color[class*=-BGC68] { + background-color: #5F87D7 +} + +div.highlight .-Color[class*=-C69] { + color: #5F87FF +} + +div.highlight .-Color[class*=-BGC69] { + background-color: #5F87FF +} + +div.highlight .-Color[class*=-C70] { + color: #5FAF00 +} + +div.highlight .-Color[class*=-BGC70] { + background-color: #5FAF00 +} + +div.highlight .-Color[class*=-C71] { + color: #5FAF5F +} + +div.highlight .-Color[class*=-BGC71] { + background-color: #5FAF5F +} + +div.highlight .-Color[class*=-C72] { + color: #5FAF87 +} + +div.highlight .-Color[class*=-BGC72] { + background-color: #5FAF87 +} + +div.highlight .-Color[class*=-C73] { + color: #5FAFAF +} + +div.highlight .-Color[class*=-BGC73] { + background-color: #5FAFAF +} + +div.highlight .-Color[class*=-C74] { + color: #5FAFD7 +} + +div.highlight .-Color[class*=-BGC74] { + background-color: #5FAFD7 +} + +div.highlight .-Color[class*=-C75] { + color: #5FAFFF +} + +div.highlight .-Color[class*=-BGC75] { + background-color: #5FAFFF +} + +div.highlight .-Color[class*=-C76] { + color: #5FD700 +} + +div.highlight .-Color[class*=-BGC76] { + background-color: #5FD700 +} + +div.highlight .-Color[class*=-C77] { + color: #5FD75F +} + +div.highlight .-Color[class*=-BGC77] { + background-color: #5FD75F +} + +div.highlight .-Color[class*=-C78] { + color: #5FD787 +} + +div.highlight .-Color[class*=-BGC78] { + background-color: #5FD787 +} + +div.highlight .-Color[class*=-C79] { + color: #5FD7AF +} + +div.highlight .-Color[class*=-BGC79] { + background-color: #5FD7AF +} + +div.highlight .-Color[class*=-C80] { + color: #5FD7D7 +} + +div.highlight .-Color[class*=-BGC80] { + background-color: #5FD7D7 +} + +div.highlight .-Color[class*=-C81] { + color: #5FD7FF +} + +div.highlight .-Color[class*=-BGC81] { + background-color: #5FD7FF +} + +div.highlight .-Color[class*=-C82] { + color: #5FFF00 +} + +div.highlight .-Color[class*=-BGC82] { + background-color: #5FFF00 +} + +div.highlight .-Color[class*=-C83] { + color: #5FFF5F +} + +div.highlight .-Color[class*=-BGC83] { + background-color: #5FFF5F +} + +div.highlight .-Color[class*=-C84] { + color: #5FFF87 +} + +div.highlight .-Color[class*=-BGC84] { + background-color: #5FFF87 +} + +div.highlight .-Color[class*=-C85] { + color: #5FFFAF +} + +div.highlight .-Color[class*=-BGC85] { + background-color: #5FFFAF +} + +div.highlight .-Color[class*=-C86] { + color: #5FFFD7 +} + +div.highlight .-Color[class*=-BGC86] { + background-color: #5FFFD7 +} + +div.highlight .-Color[class*=-C87] { + color: #5FFFFF +} + +div.highlight .-Color[class*=-BGC87] { + background-color: #5FFFFF +} + +div.highlight .-Color[class*=-C88] { + color: #870000 +} + +div.highlight .-Color[class*=-BGC88] { + background-color: #870000 +} + +div.highlight .-Color[class*=-C89] { + color: #87005F +} + +div.highlight .-Color[class*=-BGC89] { + background-color: #87005F +} + +div.highlight .-Color[class*=-C90] { + color: #870087 +} + +div.highlight .-Color[class*=-BGC90] { + background-color: #870087 +} + +div.highlight .-Color[class*=-C91] { + color: #8700AF +} + +div.highlight .-Color[class*=-BGC91] { + background-color: #8700AF +} + +div.highlight .-Color[class*=-C92] { + color: #8700D7 +} + +div.highlight .-Color[class*=-BGC92] { + background-color: #8700D7 +} + +div.highlight .-Color[class*=-C93] { + color: #8700FF +} + +div.highlight .-Color[class*=-BGC93] { + background-color: #8700FF +} + +div.highlight .-Color[class*=-C94] { + color: #875F00 +} + +div.highlight .-Color[class*=-BGC94] { + background-color: #875F00 +} + +div.highlight .-Color[class*=-C95] { + color: #875F5F +} + +div.highlight .-Color[class*=-BGC95] { + background-color: #875F5F +} + +div.highlight .-Color[class*=-C96] { + color: #875F87 +} + +div.highlight .-Color[class*=-BGC96] { + background-color: #875F87 +} + +div.highlight .-Color[class*=-C97] { + color: #875FAF +} + +div.highlight .-Color[class*=-BGC97] { + background-color: #875FAF +} + +div.highlight .-Color[class*=-C98] { + color: #875FD7 +} + +div.highlight .-Color[class*=-BGC98] { + background-color: #875FD7 +} + +div.highlight .-Color[class*=-C99] { + color: #875FFF +} + +div.highlight .-Color[class*=-BGC99] { + background-color: #875FFF +} + +div.highlight .-Color[class*=-C100] { + color: #878700 +} + +div.highlight .-Color[class*=-BGC100] { + background-color: #878700 +} + +div.highlight .-Color[class*=-C101] { + color: #87875F +} + +div.highlight .-Color[class*=-BGC101] { + background-color: #87875F +} + +div.highlight .-Color[class*=-C102] { + color: #878787 +} + +div.highlight .-Color[class*=-BGC102] { + background-color: #878787 +} + +div.highlight .-Color[class*=-C103] { + color: #8787AF +} + +div.highlight .-Color[class*=-BGC103] { + background-color: #8787AF +} + +div.highlight .-Color[class*=-C104] { + color: #8787D7 +} + +div.highlight .-Color[class*=-BGC104] { + background-color: #8787D7 +} + +div.highlight .-Color[class*=-C105] { + color: #8787FF +} + +div.highlight .-Color[class*=-BGC105] { + background-color: #8787FF +} + +div.highlight .-Color[class*=-C106] { + color: #87AF00 +} + +div.highlight .-Color[class*=-BGC106] { + background-color: #87AF00 +} + +div.highlight .-Color[class*=-C107] { + color: #87AF5F +} + +div.highlight .-Color[class*=-BGC107] { + background-color: #87AF5F +} + +div.highlight .-Color[class*=-C108] { + color: #87AF87 +} + +div.highlight .-Color[class*=-BGC108] { + background-color: #87AF87 +} + +div.highlight .-Color[class*=-C109] { + color: #87AFAF +} + +div.highlight .-Color[class*=-BGC109] { + background-color: #87AFAF +} + +div.highlight .-Color[class*=-C110] { + color: #87AFD7 +} + +div.highlight .-Color[class*=-BGC110] { + background-color: #87AFD7 +} + +div.highlight .-Color[class*=-C111] { + color: #87AFFF +} + +div.highlight .-Color[class*=-BGC111] { + background-color: #87AFFF +} + +div.highlight .-Color[class*=-C112] { + color: #87D700 +} + +div.highlight .-Color[class*=-BGC112] { + background-color: #87D700 +} + +div.highlight .-Color[class*=-C113] { + color: #87D75F +} + +div.highlight .-Color[class*=-BGC113] { + background-color: #87D75F +} + +div.highlight .-Color[class*=-C114] { + color: #87D787 +} + +div.highlight .-Color[class*=-BGC114] { + background-color: #87D787 +} + +div.highlight .-Color[class*=-C115] { + color: #87D7AF +} + +div.highlight .-Color[class*=-BGC115] { + background-color: #87D7AF +} + +div.highlight .-Color[class*=-C116] { + color: #87D7D7 +} + +div.highlight .-Color[class*=-BGC116] { + background-color: #87D7D7 +} + +div.highlight .-Color[class*=-C117] { + color: #87D7FF +} + +div.highlight .-Color[class*=-BGC117] { + background-color: #87D7FF +} + +div.highlight .-Color[class*=-C118] { + color: #87FF00 +} + +div.highlight .-Color[class*=-BGC118] { + background-color: #87FF00 +} + +div.highlight .-Color[class*=-C119] { + color: #87FF5F +} + +div.highlight .-Color[class*=-BGC119] { + background-color: #87FF5F +} + +div.highlight .-Color[class*=-C120] { + color: #87FF87 +} + +div.highlight .-Color[class*=-BGC120] { + background-color: #87FF87 +} + +div.highlight .-Color[class*=-C121] { + color: #87FFAF +} + +div.highlight .-Color[class*=-BGC121] { + background-color: #87FFAF +} + +div.highlight .-Color[class*=-C122] { + color: #87FFD7 +} + +div.highlight .-Color[class*=-BGC122] { + background-color: #87FFD7 +} + +div.highlight .-Color[class*=-C123] { + color: #87FFFF +} + +div.highlight .-Color[class*=-BGC123] { + background-color: #87FFFF +} + +div.highlight .-Color[class*=-C124] { + color: #AF0000 +} + +div.highlight .-Color[class*=-BGC124] { + background-color: #AF0000 +} + +div.highlight .-Color[class*=-C125] { + color: #AF005F +} + +div.highlight .-Color[class*=-BGC125] { + background-color: #AF005F +} + +div.highlight .-Color[class*=-C126] { + color: #AF0087 +} + +div.highlight .-Color[class*=-BGC126] { + background-color: #AF0087 +} + +div.highlight .-Color[class*=-C127] { + color: #AF00AF +} + +div.highlight .-Color[class*=-BGC127] { + background-color: #AF00AF +} + +div.highlight .-Color[class*=-C128] { + color: #AF00D7 +} + +div.highlight .-Color[class*=-BGC128] { + background-color: #AF00D7 +} + +div.highlight .-Color[class*=-C129] { + color: #AF00FF +} + +div.highlight .-Color[class*=-BGC129] { + background-color: #AF00FF +} + +div.highlight .-Color[class*=-C130] { + color: #AF5F00 +} + +div.highlight .-Color[class*=-BGC130] { + background-color: #AF5F00 +} + +div.highlight .-Color[class*=-C131] { + color: #AF5F5F +} + +div.highlight .-Color[class*=-BGC131] { + background-color: #AF5F5F +} + +div.highlight .-Color[class*=-C132] { + color: #AF5F87 +} + +div.highlight .-Color[class*=-BGC132] { + background-color: #AF5F87 +} + +div.highlight .-Color[class*=-C133] { + color: #AF5FAF +} + +div.highlight .-Color[class*=-BGC133] { + background-color: #AF5FAF +} + +div.highlight .-Color[class*=-C134] { + color: #AF5FD7 +} + +div.highlight .-Color[class*=-BGC134] { + background-color: #AF5FD7 +} + +div.highlight .-Color[class*=-C135] { + color: #AF5FFF +} + +div.highlight .-Color[class*=-BGC135] { + background-color: #AF5FFF +} + +div.highlight .-Color[class*=-C136] { + color: #AF8700 +} + +div.highlight .-Color[class*=-BGC136] { + background-color: #AF8700 +} + +div.highlight .-Color[class*=-C137] { + color: #AF875F +} + +div.highlight .-Color[class*=-BGC137] { + background-color: #AF875F +} + +div.highlight .-Color[class*=-C138] { + color: #AF8787 +} + +div.highlight .-Color[class*=-BGC138] { + background-color: #AF8787 +} + +div.highlight .-Color[class*=-C139] { + color: #AF87AF +} + +div.highlight .-Color[class*=-BGC139] { + background-color: #AF87AF +} + +div.highlight .-Color[class*=-C140] { + color: #AF87D7 +} + +div.highlight .-Color[class*=-BGC140] { + background-color: #AF87D7 +} + +div.highlight .-Color[class*=-C141] { + color: #AF87FF +} + +div.highlight .-Color[class*=-BGC141] { + background-color: #AF87FF +} + +div.highlight .-Color[class*=-C142] { + color: #AFAF00 +} + +div.highlight .-Color[class*=-BGC142] { + background-color: #AFAF00 +} + +div.highlight .-Color[class*=-C143] { + color: #AFAF5F +} + +div.highlight .-Color[class*=-BGC143] { + background-color: #AFAF5F +} + +div.highlight .-Color[class*=-C144] { + color: #AFAF87 +} + +div.highlight .-Color[class*=-BGC144] { + background-color: #AFAF87 +} + +div.highlight .-Color[class*=-C145] { + color: #AFAFAF +} + +div.highlight .-Color[class*=-BGC145] { + background-color: #AFAFAF +} + +div.highlight .-Color[class*=-C146] { + color: #AFAFD7 +} + +div.highlight .-Color[class*=-BGC146] { + background-color: #AFAFD7 +} + +div.highlight .-Color[class*=-C147] { + color: #AFAFFF +} + +div.highlight .-Color[class*=-BGC147] { + background-color: #AFAFFF +} + +div.highlight .-Color[class*=-C148] { + color: #AFD700 +} + +div.highlight .-Color[class*=-BGC148] { + background-color: #AFD700 +} + +div.highlight .-Color[class*=-C149] { + color: #AFD75F +} + +div.highlight .-Color[class*=-BGC149] { + background-color: #AFD75F +} + +div.highlight .-Color[class*=-C150] { + color: #AFD787 +} + +div.highlight .-Color[class*=-BGC150] { + background-color: #AFD787 +} + +div.highlight .-Color[class*=-C151] { + color: #AFD7AF +} + +div.highlight .-Color[class*=-BGC151] { + background-color: #AFD7AF +} + +div.highlight .-Color[class*=-C152] { + color: #AFD7D7 +} + +div.highlight .-Color[class*=-BGC152] { + background-color: #AFD7D7 +} + +div.highlight .-Color[class*=-C153] { + color: #AFD7FF +} + +div.highlight .-Color[class*=-BGC153] { + background-color: #AFD7FF +} + +div.highlight .-Color[class*=-C154] { + color: #AFFF00 +} + +div.highlight .-Color[class*=-BGC154] { + background-color: #AFFF00 +} + +div.highlight .-Color[class*=-C155] { + color: #AFFF5F +} + +div.highlight .-Color[class*=-BGC155] { + background-color: #AFFF5F +} + +div.highlight .-Color[class*=-C156] { + color: #AFFF87 +} + +div.highlight .-Color[class*=-BGC156] { + background-color: #AFFF87 +} + +div.highlight .-Color[class*=-C157] { + color: #AFFFAF +} + +div.highlight .-Color[class*=-BGC157] { + background-color: #AFFFAF +} + +div.highlight .-Color[class*=-C158] { + color: #AFFFD7 +} + +div.highlight .-Color[class*=-BGC158] { + background-color: #AFFFD7 +} + +div.highlight .-Color[class*=-C159] { + color: #AFFFFF +} + +div.highlight .-Color[class*=-BGC159] { + background-color: #AFFFFF +} + +div.highlight .-Color[class*=-C160] { + color: #D70000 +} + +div.highlight .-Color[class*=-BGC160] { + background-color: #D70000 +} + +div.highlight .-Color[class*=-C161] { + color: #D7005F +} + +div.highlight .-Color[class*=-BGC161] { + background-color: #D7005F +} + +div.highlight .-Color[class*=-C162] { + color: #D70087 +} + +div.highlight .-Color[class*=-BGC162] { + background-color: #D70087 +} + +div.highlight .-Color[class*=-C163] { + color: #D700AF +} + +div.highlight .-Color[class*=-BGC163] { + background-color: #D700AF +} + +div.highlight .-Color[class*=-C164] { + color: #D700D7 +} + +div.highlight .-Color[class*=-BGC164] { + background-color: #D700D7 +} + +div.highlight .-Color[class*=-C165] { + color: #D700FF +} + +div.highlight .-Color[class*=-BGC165] { + background-color: #D700FF +} + +div.highlight .-Color[class*=-C166] { + color: #D75F00 +} + +div.highlight .-Color[class*=-BGC166] { + background-color: #D75F00 +} + +div.highlight .-Color[class*=-C167] { + color: #D75F5F +} + +div.highlight .-Color[class*=-BGC167] { + background-color: #D75F5F +} + +div.highlight .-Color[class*=-C168] { + color: #D75F87 +} + +div.highlight .-Color[class*=-BGC168] { + background-color: #D75F87 +} + +div.highlight .-Color[class*=-C169] { + color: #D75FAF +} + +div.highlight .-Color[class*=-BGC169] { + background-color: #D75FAF +} + +div.highlight .-Color[class*=-C170] { + color: #D75FD7 +} + +div.highlight .-Color[class*=-BGC170] { + background-color: #D75FD7 +} + +div.highlight .-Color[class*=-C171] { + color: #D75FFF +} + +div.highlight .-Color[class*=-BGC171] { + background-color: #D75FFF +} + +div.highlight .-Color[class*=-C172] { + color: #D78700 +} + +div.highlight .-Color[class*=-BGC172] { + background-color: #D78700 +} + +div.highlight .-Color[class*=-C173] { + color: #D7875F +} + +div.highlight .-Color[class*=-BGC173] { + background-color: #D7875F +} + +div.highlight .-Color[class*=-C174] { + color: #D78787 +} + +div.highlight .-Color[class*=-BGC174] { + background-color: #D78787 +} + +div.highlight .-Color[class*=-C175] { + color: #D787AF +} + +div.highlight .-Color[class*=-BGC175] { + background-color: #D787AF +} + +div.highlight .-Color[class*=-C176] { + color: #D787D7 +} + +div.highlight .-Color[class*=-BGC176] { + background-color: #D787D7 +} + +div.highlight .-Color[class*=-C177] { + color: #D787FF +} + +div.highlight .-Color[class*=-BGC177] { + background-color: #D787FF +} + +div.highlight .-Color[class*=-C178] { + color: #D7AF00 +} + +div.highlight .-Color[class*=-BGC178] { + background-color: #D7AF00 +} + +div.highlight .-Color[class*=-C179] { + color: #D7AF5F +} + +div.highlight .-Color[class*=-BGC179] { + background-color: #D7AF5F +} + +div.highlight .-Color[class*=-C180] { + color: #D7AF87 +} + +div.highlight .-Color[class*=-BGC180] { + background-color: #D7AF87 +} + +div.highlight .-Color[class*=-C181] { + color: #D7AFAF +} + +div.highlight .-Color[class*=-BGC181] { + background-color: #D7AFAF +} + +div.highlight .-Color[class*=-C182] { + color: #D7AFD7 +} + +div.highlight .-Color[class*=-BGC182] { + background-color: #D7AFD7 +} + +div.highlight .-Color[class*=-C183] { + color: #D7AFFF +} + +div.highlight .-Color[class*=-BGC183] { + background-color: #D7AFFF +} + +div.highlight .-Color[class*=-C184] { + color: #D7D700 +} + +div.highlight .-Color[class*=-BGC184] { + background-color: #D7D700 +} + +div.highlight .-Color[class*=-C185] { + color: #D7D75F +} + +div.highlight .-Color[class*=-BGC185] { + background-color: #D7D75F +} + +div.highlight .-Color[class*=-C186] { + color: #D7D787 +} + +div.highlight .-Color[class*=-BGC186] { + background-color: #D7D787 +} + +div.highlight .-Color[class*=-C187] { + color: #D7D7AF +} + +div.highlight .-Color[class*=-BGC187] { + background-color: #D7D7AF +} + +div.highlight .-Color[class*=-C188] { + color: #D7D7D7 +} + +div.highlight .-Color[class*=-BGC188] { + background-color: #D7D7D7 +} + +div.highlight .-Color[class*=-C189] { + color: #D7D7FF +} + +div.highlight .-Color[class*=-BGC189] { + background-color: #D7D7FF +} + +div.highlight .-Color[class*=-C190] { + color: #D7FF00 +} + +div.highlight .-Color[class*=-BGC190] { + background-color: #D7FF00 +} + +div.highlight .-Color[class*=-C191] { + color: #D7FF5F +} + +div.highlight .-Color[class*=-BGC191] { + background-color: #D7FF5F +} + +div.highlight .-Color[class*=-C192] { + color: #D7FF87 +} + +div.highlight .-Color[class*=-BGC192] { + background-color: #D7FF87 +} + +div.highlight .-Color[class*=-C193] { + color: #D7FFAF +} + +div.highlight .-Color[class*=-BGC193] { + background-color: #D7FFAF +} + +div.highlight .-Color[class*=-C194] { + color: #D7FFD7 +} + +div.highlight .-Color[class*=-BGC194] { + background-color: #D7FFD7 +} + +div.highlight .-Color[class*=-C195] { + color: #D7FFFF +} + +div.highlight .-Color[class*=-BGC195] { + background-color: #D7FFFF +} + +div.highlight .-Color[class*=-C196] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC196] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C197] { + color: #FF005F +} + +div.highlight .-Color[class*=-BGC197] { + background-color: #FF005F +} + +div.highlight .-Color[class*=-C198] { + color: #FF0087 +} + +div.highlight .-Color[class*=-BGC198] { + background-color: #FF0087 +} + +div.highlight .-Color[class*=-C199] { + color: #FF00AF +} + +div.highlight .-Color[class*=-BGC199] { + background-color: #FF00AF +} + +div.highlight .-Color[class*=-C200] { + color: #FF00D7 +} + +div.highlight .-Color[class*=-BGC200] { + background-color: #FF00D7 +} + +div.highlight .-Color[class*=-C201] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC201] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C202] { + color: #FF5F00 +} + +div.highlight .-Color[class*=-BGC202] { + background-color: #FF5F00 +} + +div.highlight .-Color[class*=-C203] { + color: #FF5F5F +} + +div.highlight .-Color[class*=-BGC203] { + background-color: #FF5F5F +} + +div.highlight .-Color[class*=-C204] { + color: #FF5F87 +} + +div.highlight .-Color[class*=-BGC204] { + background-color: #FF5F87 +} + +div.highlight .-Color[class*=-C205] { + color: #FF5FAF +} + +div.highlight .-Color[class*=-BGC205] { + background-color: #FF5FAF +} + +div.highlight .-Color[class*=-C206] { + color: #FF5FD7 +} + +div.highlight .-Color[class*=-BGC206] { + background-color: #FF5FD7 +} + +div.highlight .-Color[class*=-C207] { + color: #FF5FFF +} + +div.highlight .-Color[class*=-BGC207] { + background-color: #FF5FFF +} + +div.highlight .-Color[class*=-C208] { + color: #FF8700 +} + +div.highlight .-Color[class*=-BGC208] { + background-color: #FF8700 +} + +div.highlight .-Color[class*=-C209] { + color: #FF875F +} + +div.highlight .-Color[class*=-BGC209] { + background-color: #FF875F +} + +div.highlight .-Color[class*=-C210] { + color: #FF8787 +} + +div.highlight .-Color[class*=-BGC210] { + background-color: #FF8787 +} + +div.highlight .-Color[class*=-C211] { + color: #FF87AF +} + +div.highlight .-Color[class*=-BGC211] { + background-color: #FF87AF +} + +div.highlight .-Color[class*=-C212] { + color: #FF87D7 +} + +div.highlight .-Color[class*=-BGC212] { + background-color: #FF87D7 +} + +div.highlight .-Color[class*=-C213] { + color: #FF87FF +} + +div.highlight .-Color[class*=-BGC213] { + background-color: #FF87FF +} + +div.highlight .-Color[class*=-C214] { + color: #FFAF00 +} + +div.highlight .-Color[class*=-BGC214] { + background-color: #FFAF00 +} + +div.highlight .-Color[class*=-C215] { + color: #FFAF5F +} + +div.highlight .-Color[class*=-BGC215] { + background-color: #FFAF5F +} + +div.highlight .-Color[class*=-C216] { + color: #FFAF87 +} + +div.highlight .-Color[class*=-BGC216] { + background-color: #FFAF87 +} + +div.highlight .-Color[class*=-C217] { + color: #FFAFAF +} + +div.highlight .-Color[class*=-BGC217] { + background-color: #FFAFAF +} + +div.highlight .-Color[class*=-C218] { + color: #FFAFD7 +} + +div.highlight .-Color[class*=-BGC218] { + background-color: #FFAFD7 +} + +div.highlight .-Color[class*=-C219] { + color: #FFAFFF +} + +div.highlight .-Color[class*=-BGC219] { + background-color: #FFAFFF +} + +div.highlight .-Color[class*=-C220] { + color: #FFD700 +} + +div.highlight .-Color[class*=-BGC220] { + background-color: #FFD700 +} + +div.highlight .-Color[class*=-C221] { + color: #FFD75F +} + +div.highlight .-Color[class*=-BGC221] { + background-color: #FFD75F +} + +div.highlight .-Color[class*=-C222] { + color: #FFD787 +} + +div.highlight .-Color[class*=-BGC222] { + background-color: #FFD787 +} + +div.highlight .-Color[class*=-C223] { + color: #FFD7AF +} + +div.highlight .-Color[class*=-BGC223] { + background-color: #FFD7AF +} + +div.highlight .-Color[class*=-C224] { + color: #FFD7D7 +} + +div.highlight .-Color[class*=-BGC224] { + background-color: #FFD7D7 +} + +div.highlight .-Color[class*=-C225] { + color: #FFD7FF +} + +div.highlight .-Color[class*=-BGC225] { + background-color: #FFD7FF +} + +div.highlight .-Color[class*=-C226] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC226] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C227] { + color: #FFFF5F +} + +div.highlight .-Color[class*=-BGC227] { + background-color: #FFFF5F +} + +div.highlight .-Color[class*=-C228] { + color: #FFFF87 +} + +div.highlight .-Color[class*=-BGC228] { + background-color: #FFFF87 +} + +div.highlight .-Color[class*=-C229] { + color: #FFFFAF +} + +div.highlight .-Color[class*=-BGC229] { + background-color: #FFFFAF +} + +div.highlight .-Color[class*=-C230] { + color: #FFFFD7 +} + +div.highlight .-Color[class*=-BGC230] { + background-color: #FFFFD7 +} + +div.highlight .-Color[class*=-C231] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC231] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C232] { + color: #080808 +} + +div.highlight .-Color[class*=-BGC232] { + background-color: #080808 +} + +div.highlight .-Color[class*=-C233] { + color: #121212 +} + +div.highlight .-Color[class*=-BGC233] { + background-color: #121212 +} + +div.highlight .-Color[class*=-C234] { + color: #1C1C1C +} + +div.highlight .-Color[class*=-BGC234] { + background-color: #1C1C1C +} + +div.highlight .-Color[class*=-C235] { + color: #262626 +} + +div.highlight .-Color[class*=-BGC235] { + background-color: #262626 +} + +div.highlight .-Color[class*=-C236] { + color: #303030 +} + +div.highlight .-Color[class*=-BGC236] { + background-color: #303030 +} + +div.highlight .-Color[class*=-C237] { + color: #3A3A3A +} + +div.highlight .-Color[class*=-BGC237] { + background-color: #3A3A3A +} + +div.highlight .-Color[class*=-C238] { + color: #444444 +} + +div.highlight .-Color[class*=-BGC238] { + background-color: #444444 +} + +div.highlight .-Color[class*=-C239] { + color: #4E4E4E +} + +div.highlight .-Color[class*=-BGC239] { + background-color: #4E4E4E +} + +div.highlight .-Color[class*=-C240] { + color: #585858 +} + +div.highlight .-Color[class*=-BGC240] { + background-color: #585858 +} + +div.highlight .-Color[class*=-C241] { + color: #626262 +} + +div.highlight .-Color[class*=-BGC241] { + background-color: #626262 +} + +div.highlight .-Color[class*=-C242] { + color: #6C6C6C +} + +div.highlight .-Color[class*=-BGC242] { + background-color: #6C6C6C +} + +div.highlight .-Color[class*=-C243] { + color: #767676 +} + +div.highlight .-Color[class*=-BGC243] { + background-color: #767676 +} + +div.highlight .-Color[class*=-C244] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC244] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C245] { + color: #8A8A8A +} + +div.highlight .-Color[class*=-BGC245] { + background-color: #8A8A8A +} + +div.highlight .-Color[class*=-C246] { + color: #949494 +} + +div.highlight .-Color[class*=-BGC246] { + background-color: #949494 +} + +div.highlight .-Color[class*=-C247] { + color: #9E9E9E +} + +div.highlight .-Color[class*=-BGC247] { + background-color: #9E9E9E +} + +div.highlight .-Color[class*=-C248] { + color: #A8A8A8 +} + +div.highlight .-Color[class*=-BGC248] { + background-color: #A8A8A8 +} + +div.highlight .-Color[class*=-C249] { + color: #B2B2B2 +} + +div.highlight .-Color[class*=-BGC249] { + background-color: #B2B2B2 +} + +div.highlight .-Color[class*=-C250] { + color: #BCBCBC +} + +div.highlight .-Color[class*=-BGC250] { + background-color: #BCBCBC +} + +div.highlight .-Color[class*=-C251] { + color: #C6C6C6 +} + +div.highlight .-Color[class*=-BGC251] { + background-color: #C6C6C6 +} + +div.highlight .-Color[class*=-C252] { + color: #D0D0D0 +} + +div.highlight .-Color[class*=-BGC252] { + background-color: #D0D0D0 +} + +div.highlight .-Color[class*=-C253] { + color: #DADADA +} + +div.highlight .-Color[class*=-BGC253] { + background-color: #DADADA +} + +div.highlight .-Color[class*=-C254] { + color: #E4E4E4 +} + +div.highlight .-Color[class*=-BGC254] { + background-color: #E4E4E4 +} + +div.highlight .-Color[class*=-C255] { + color: #EEEEEE +} + +div.highlight .-Color[class*=-BGC255] { + background-color: #EEEEEE +} diff --git a/branch/main/_static/plus.png b/branch/main/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/branch/main/_static/plus.png differ diff --git a/branch/main/_static/pygments.css b/branch/main/_static/pygments.css new file mode 100644 index 0000000..84ab303 --- /dev/null +++ b/branch/main/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/branch/main/_static/searchtools.js b/branch/main/_static/searchtools.js new file mode 100644 index 0000000..b08d58c --- /dev/null +++ b/branch/main/_static/searchtools.js @@ -0,0 +1,620 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + "Search finished, found ${resultCount} page(s) matching the search query." + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/branch/main/_static/sphinx_highlight.js b/branch/main/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/branch/main/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/branch/main/_static/sphinx_lesson.css b/branch/main/_static/sphinx_lesson.css new file mode 100644 index 0000000..14b20c6 --- /dev/null +++ b/branch/main/_static/sphinx_lesson.css @@ -0,0 +1,51 @@ +/* sphinx_lesson.css */ + +body.wy-body-for-nav img.with-border { + border: 2px solid; +} + +.rst-content .admonition-no-content { + padding-bottom: 0px; +} + +.rst-content .demo > .admonition-title::before { + content: "\01F440"; /* Eyes */ } +.rst-content .type-along > .admonition-title::before { + content: "\02328\0FE0F"; /* Keyboard */ } +.rst-content .exercise > .admonition-title::before { + content: "\0270D\0FE0F"; /* Hand */ } +.rst-content .solution > .admonition-title::before { + content: "\02714\0FE0E"; /* Check mark */ } +.rst-content .homework > .admonition-title::before { + content: "\01F4DD"; /* Memo */ } +.rst-content .discussion > .admonition-title::before { + content: "\01F4AC"; /* Speech balloon */ } +.rst-content .questions > .admonition-title::before { + content: "\02753\0FE0E"; /* Question mark */ } +.rst-content .prerequisites > .admonition-title::before { + content: "\02699"; /* Gear */ } +.rst-content .seealso > .admonition-title::before { + content: "\027A1\0FE0E"; /* Question mark */ } + + +/* instructor-note */ +.rst-content .instructor-note { + background: #e7e7e7; +} +.rst-content .instructor-note > .admonition-title { + background: #6a6a6a; +} +.rst-content .instructor-note > .admonition-title::before { + content: ""; +} + + +/* sphinx_toggle_button, make the font white */ +.rst-content .toggle.admonition button.toggle-button { + color: white; +} + +/* sphinx-togglebutton, remove underflow when toggled to hidden mode */ +.rst-content .admonition.toggle-hidden { + padding-bottom: 0px; +} diff --git a/branch/main/_static/sphinx_rtd_theme_ext_color_contrast.css b/branch/main/_static/sphinx_rtd_theme_ext_color_contrast.css new file mode 100644 index 0000000..e68feb8 --- /dev/null +++ b/branch/main/_static/sphinx_rtd_theme_ext_color_contrast.css @@ -0,0 +1,47 @@ +/* The following are for web accessibility of sphinx_rtd_theme: they + * solve some of the most frequent contrast issues. Remove when this + * solved: + * https://github.com/readthedocs/sphinx_rtd_theme/issues/971 + */ +/* background: #fcfcfc, note boxes #E7F2FA */ +a { color: #2573A7; } /* original #2980B9, #1F5C84; */ +body { color: #242424; } /* original #404040, #383838 */ +.wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a { + color: #ffffff; +} /* original #fcfcfc */ +footer { color: #737373; } /* original gray=#808080*/ +footer span.commit code, footer span.commit .rst-content tt, .rst-content footer span.commit tt { + color: #737373; +} /* original gray=#808080*/ +.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal { + color: #AB2314; +} +/* Sidebar background */ +.wy-side-nav-search { background-color: #277CB4;} + +/* Same, but for pygments */ +.highlight .ch { color: #3E7A89; } /* #! line */ +.highlight .c1 { color: #3E7A89; } /* also comments */ +.highlight .nv { color: #AD3ECC; } /* variable */ +.highlight .gp { color: #B45608; } /* prompt character, $*/ +.highlight .si { color: #3975B1; } /* ${} variable text */ +.highlight .nc { color: #0C78A7; } + +/* Sphinx admonitions */ +/* warning */ +.wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .rst-content .wy-alert-warning.admonition .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title, .rst-content .wy-alert-warning.admonition .admonition-title { + background: #B15E16; } +/* important */ +.wy-alert.wy-alert-success .wy-alert-title, .rst-content .wy-alert-success.note .wy-alert-title, .rst-content .wy-alert-success.attention .wy-alert-title, .rst-content .wy-alert-success.caution .wy-alert-title, .rst-content .wy-alert-success.danger .wy-alert-title, .rst-content .wy-alert-success.error .wy-alert-title, .rst-content .hint .wy-alert-title, .rst-content .important .wy-alert-title, .rst-content .tip .wy-alert-title, .rst-content .wy-alert-success.warning .wy-alert-title, .rst-content .wy-alert-success.seealso .wy-alert-title, .rst-content .wy-alert-success.admonition-todo .wy-alert-title, .rst-content .wy-alert-success.admonition .wy-alert-title, .wy-alert.wy-alert-success .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-success .admonition-title, .rst-content .wy-alert-success.note .admonition-title, .rst-content .wy-alert-success.attention .admonition-title, .rst-content .wy-alert-success.caution .admonition-title, .rst-content .wy-alert-success.danger .admonition-title, .rst-content .wy-alert-success.error .admonition-title, .rst-content .hint .admonition-title, .rst-content .important .admonition-title, .rst-content .tip .admonition-title, .rst-content .wy-alert-success.warning .admonition-title, .rst-content .wy-alert-success.seealso .admonition-title, .rst-content .wy-alert-success.admonition-todo .admonition-title, .rst-content .wy-alert-success.admonition .admonition-title { + background: #12826C; } +/* seealso, note, etc */ +.wy-alert.wy-alert-info .wy-alert-title, .rst-content .note .wy-alert-title, .rst-content .wy-alert-info.attention .wy-alert-title, .rst-content .wy-alert-info.caution .wy-alert-title, .rst-content .wy-alert-info.danger .wy-alert-title, .rst-content .wy-alert-info.error .wy-alert-title, .rst-content .wy-alert-info.hint .wy-alert-title, .rst-content .wy-alert-info.important .wy-alert-title, .rst-content .wy-alert-info.tip .wy-alert-title, .rst-content .wy-alert-info.warning .wy-alert-title, .rst-content .seealso .wy-alert-title, .rst-content .wy-alert-info.admonition-todo .wy-alert-title, .rst-content .wy-alert-info.admonition .wy-alert-title, .wy-alert.wy-alert-info .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-info .admonition-title, .rst-content .note .admonition-title, .rst-content .wy-alert-info.attention .admonition-title, .rst-content .wy-alert-info.caution .admonition-title, .rst-content .wy-alert-info.danger .admonition-title, .rst-content .wy-alert-info.error .admonition-title, .rst-content .wy-alert-info.hint .admonition-title, .rst-content .wy-alert-info.important .admonition-title, .rst-content .wy-alert-info.tip .admonition-title, .rst-content .wy-alert-info.warning .admonition-title, .rst-content .seealso .admonition-title, .rst-content .wy-alert-info.admonition-todo .admonition-title, .rst-content .wy-alert-info.admonition .admonition-title { + background: #277CB4; } +/* error, danger */ +.rst-content .danger .admonition-title, .rst-content .danger .wy-alert-title, .rst-content .error .admonition-title, .rst-content .error .wy-alert-title, .rst-content .wy-alert-danger.admonition-todo .admonition-title, .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, .rst-content .wy-alert-danger.admonition .admonition-title, .rst-content .wy-alert-danger.admonition .wy-alert-title, .rst-content .wy-alert-danger.attention .admonition-title, .rst-content .wy-alert-danger.attention .wy-alert-title, .rst-content .wy-alert-danger.caution .admonition-title, .rst-content .wy-alert-danger.caution .wy-alert-title, .rst-content .wy-alert-danger.hint .admonition-title, .rst-content .wy-alert-danger.hint .wy-alert-title, .rst-content .wy-alert-danger.important .admonition-title, .rst-content .wy-alert-danger.important .wy-alert-title, .rst-content .wy-alert-danger.note .admonition-title, .rst-content .wy-alert-danger.note .wy-alert-title, .rst-content .wy-alert-danger.seealso .admonition-title, .rst-content .wy-alert-danger.seealso .wy-alert-title, .rst-content .wy-alert-danger.tip .admonition-title, .rst-content .wy-alert-danger.tip .wy-alert-title, .rst-content .wy-alert-danger.warning .admonition-title, .rst-content .wy-alert-danger.warning .wy-alert-title, .rst-content .wy-alert.wy-alert-danger .admonition-title, .wy-alert.wy-alert-danger .rst-content .admonition-title, .wy-alert.wy-alert-danger .wy-alert-title { + background: #e31704; +} + +/* Generic admonition titles */ +.wy-alert-title, .rst-content .admonition-title { + background: #277CB4; } diff --git a/branch/main/_static/style.css b/branch/main/_static/style.css new file mode 100644 index 0000000..59e60ee --- /dev/null +++ b/branch/main/_static/style.css @@ -0,0 +1,6 @@ +.rst-content .objectives { + background: #fee0d2; +} +.rst-content .objectives > .admonition-title { + background: #fc9272; +} diff --git a/branch/main/_static/tabs.css b/branch/main/_static/tabs.css new file mode 100644 index 0000000..957ba60 --- /dev/null +++ b/branch/main/_static/tabs.css @@ -0,0 +1,89 @@ +.sphinx-tabs { + margin-bottom: 1rem; +} + +[role="tablist"] { + border-bottom: 1px solid #a0b3bf; +} + +.sphinx-tabs-tab { + position: relative; + font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif; + color: #1D5C87; + line-height: 24px; + margin: 0; + font-size: 16px; + font-weight: 400; + background-color: rgba(255, 255, 255, 0); + border-radius: 5px 5px 0 0; + border: 0; + padding: 1rem 1.5rem; + margin-bottom: 0; +} + +.sphinx-tabs-tab[aria-selected="true"] { + font-weight: 700; + border: 1px solid #a0b3bf; + border-bottom: 1px solid white; + margin: -1px; + background-color: white; +} + +.sphinx-tabs-tab:focus { + z-index: 1; + outline-offset: 1px; +} + +.sphinx-tabs-panel { + position: relative; + padding: 1rem; + border: 1px solid #a0b3bf; + margin: 0px -1px -1px -1px; + border-radius: 0 0 5px 5px; + border-top: 0; + background: white; +} + +.sphinx-tabs-panel.code-tab { + padding: 0.4rem; +} + +.sphinx-tab img { + margin-bottom: 24 px; +} + +/* Dark theme preference styling */ + +@media (prefers-color-scheme: dark) { + body[data-theme="auto"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); + } + + body[data-theme="auto"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); + } + + body[data-theme="auto"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 1px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); + } +} + +/* Explicit dark theme styling */ + +body[data-theme="dark"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); +} + +body[data-theme="dark"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); +} + +body[data-theme="dark"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 2px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); +} diff --git a/branch/main/_static/tabs.js b/branch/main/_static/tabs.js new file mode 100644 index 0000000..48dc303 --- /dev/null +++ b/branch/main/_static/tabs.js @@ -0,0 +1,145 @@ +try { + var session = window.sessionStorage || {}; +} catch (e) { + var session = {}; +} + +window.addEventListener("DOMContentLoaded", () => { + const allTabs = document.querySelectorAll('.sphinx-tabs-tab'); + const tabLists = document.querySelectorAll('[role="tablist"]'); + + allTabs.forEach(tab => { + tab.addEventListener("click", changeTabs); + }); + + tabLists.forEach(tabList => { + tabList.addEventListener("keydown", keyTabs); + }); + + // Restore group tab selection from session + const lastSelected = session.getItem('sphinx-tabs-last-selected'); + if (lastSelected != null) selectNamedTabs(lastSelected); +}); + +/** + * Key focus left and right between sibling elements using arrows + * @param {Node} e the element in focus when key was pressed + */ +function keyTabs(e) { + const tab = e.target; + let nextTab = null; + if (e.keyCode === 39 || e.keyCode === 37) { + tab.setAttribute("tabindex", -1); + // Move right + if (e.keyCode === 39) { + nextTab = tab.nextElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.firstElementChild; + } + // Move left + } else if (e.keyCode === 37) { + nextTab = tab.previousElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.lastElementChild; + } + } + } + + if (nextTab !== null) { + nextTab.setAttribute("tabindex", 0); + nextTab.focus(); + } +} + +/** + * Select or deselect clicked tab. If a group tab + * is selected, also select tab in other tabLists. + * @param {Node} e the element that was clicked + */ +function changeTabs(e) { + // Use this instead of the element that was clicked, in case it's a child + const notSelected = this.getAttribute("aria-selected") === "false"; + const positionBefore = this.parentNode.getBoundingClientRect().top; + const notClosable = !this.parentNode.classList.contains("closeable"); + + deselectTabList(this); + + if (notSelected || notClosable) { + selectTab(this); + const name = this.getAttribute("name"); + selectNamedTabs(name, this.id); + + if (this.classList.contains("group-tab")) { + // Persist during session + session.setItem('sphinx-tabs-last-selected', name); + } + } + + const positionAfter = this.parentNode.getBoundingClientRect().top; + const positionDelta = positionAfter - positionBefore; + // Scroll to offset content resizing + window.scrollTo(0, window.scrollY + positionDelta); +} + +/** + * Select tab and show associated panel. + * @param {Node} tab tab to select + */ +function selectTab(tab) { + tab.setAttribute("aria-selected", true); + + // Show the associated panel + document + .getElementById(tab.getAttribute("aria-controls")) + .removeAttribute("hidden"); +} + +/** + * Hide the panels associated with all tabs within the + * tablist containing this tab. + * @param {Node} tab a tab within the tablist to deselect + */ +function deselectTabList(tab) { + const parent = tab.parentNode; + const grandparent = parent.parentNode; + + Array.from(parent.children) + .forEach(t => t.setAttribute("aria-selected", false)); + + Array.from(grandparent.children) + .slice(1) // Skip tablist + .forEach(panel => panel.setAttribute("hidden", true)); +} + +/** + * Select grouped tabs with the same name, but no the tab + * with the given id. + * @param {Node} name name of grouped tab to be selected + * @param {Node} clickedId id of clicked tab + */ +function selectNamedTabs(name, clickedId=null) { + const groupedTabs = document.querySelectorAll(`.sphinx-tabs-tab[name="${name}"]`); + const tabLists = Array.from(groupedTabs).map(tab => tab.parentNode); + + tabLists + .forEach(tabList => { + // Don't want to change the tabList containing the clicked tab + const clickedTab = tabList.querySelector(`[id="${clickedId}"]`); + if (clickedTab === null ) { + // Select first tab with matching name + const tab = tabList.querySelector(`.sphinx-tabs-tab[name="${name}"]`); + deselectTabList(tab); + selectTab(tab); + } + }) +} + +if (typeof exports === 'undefined') { + exports = {}; +} + +exports.keyTabs = keyTabs; +exports.changeTabs = changeTabs; +exports.selectTab = selectTab; +exports.deselectTabList = deselectTabList; +exports.selectNamedTabs = selectNamedTabs; diff --git a/branch/main/_static/term_role_formatting.css b/branch/main/_static/term_role_formatting.css new file mode 100644 index 0000000..0b66095 --- /dev/null +++ b/branch/main/_static/term_role_formatting.css @@ -0,0 +1,4 @@ +/* Make terms bold */ +a.reference span.std-term { + font-weight: bold; +} diff --git a/branch/main/_static/togglebutton.css b/branch/main/_static/togglebutton.css new file mode 100644 index 0000000..54a6787 --- /dev/null +++ b/branch/main/_static/togglebutton.css @@ -0,0 +1,160 @@ +/** + * Admonition-based toggles + */ + +/* Visibility of the target */ +.admonition.toggle .admonition-title ~ * { + transition: opacity .3s, height .3s; +} + +/* Toggle buttons inside admonitions so we see the title */ +.admonition.toggle { + position: relative; +} + +/* Titles should cut off earlier to avoid overlapping w/ button */ +.admonition.toggle .admonition-title { + padding-right: 25%; + cursor: pointer; +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:hover { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 1%); +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:active { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 3%); +} + +/* Remove extra whitespace below the admonition title when hidden */ +.admonition.toggle-hidden { + padding-bottom: 0; +} + +.admonition.toggle-hidden .admonition-title { + margin-bottom: 0; +} + +/* hides all the content of a page until de-toggled */ +.admonition.toggle-hidden .admonition-title ~ * { + height: 0; + margin: 0; + opacity: 0; + visibility: hidden; +} + +/* General button style and position*/ +button.toggle-button { + /** + * Background and shape. By default there's no background + * but users can style as they wish + */ + background: none; + border: none; + outline: none; + + /* Positioning just inside the admonition title */ + position: absolute; + right: 0.5em; + padding: 0px; + border: none; + outline: none; +} + +/* Display the toggle hint on wide screens */ +@media (min-width: 768px) { + button.toggle-button.toggle-button-hidden:before { + content: attr(data-toggle-hint); /* This will be filled in by JS */ + font-size: .8em; + align-self: center; + } +} + +/* Icon behavior */ +.tb-icon { + transition: transform .2s ease-out; + height: 1.5em; + width: 1.5em; + stroke: currentColor; /* So that we inherit the color of other text */ +} + +/* The icon should point right when closed, down when open. */ +/* Open */ +.admonition.toggle button .tb-icon { + transform: rotate(90deg); +} + +/* Closed */ +.admonition.toggle button.toggle-button-hidden .tb-icon { + transform: rotate(0deg); +} + +/* With details toggles, we don't rotate the icon so it points right */ +details.toggle-details .tb-icon { + height: 1.4em; + width: 1.4em; + margin-top: 0.1em; /* To center the button vertically */ +} + + +/** + * Details-based toggles. + * In this case, we wrap elements with `.toggle` in a details block. + */ + +/* Details blocks */ +details.toggle-details { + margin: 1em 0; +} + + +details.toggle-details summary { + display: flex; + align-items: center; + cursor: pointer; + list-style: none; + border-radius: .2em; + border-left: 3px solid #1976d2; + background-color: rgb(204 204 204 / 10%); + padding: 0.2em 0.7em 0.3em 0.5em; /* Less padding on left because the SVG has left margin */ + font-size: 0.9em; +} + +details.toggle-details summary:hover { + background-color: rgb(204 204 204 / 20%); +} + +details.toggle-details summary:active { + background: rgb(204 204 204 / 28%); +} + +.toggle-details__summary-text { + margin-left: 0.2em; +} + +details.toggle-details[open] summary { + margin-bottom: .5em; +} + +details.toggle-details[open] summary .tb-icon { + transform: rotate(90deg); +} + +details.toggle-details[open] summary ~ * { + animation: toggle-fade-in .3s ease-out; +} + +@keyframes toggle-fade-in { + from {opacity: 0%;} + to {opacity: 100%;} +} + +/* Print rules - we hide all toggle button elements at print */ +@media print { + /* Always hide the summary so the button doesn't show up */ + details.toggle-details summary { + display: none; + } +} \ No newline at end of file diff --git a/branch/main/_static/togglebutton.js b/branch/main/_static/togglebutton.js new file mode 100644 index 0000000..215a7ee --- /dev/null +++ b/branch/main/_static/togglebutton.js @@ -0,0 +1,187 @@ +/** + * Add Toggle Buttons to elements + */ + +let toggleChevron = ` + + + +`; + +var initToggleItems = () => { + var itemsToToggle = document.querySelectorAll(togglebuttonSelector); + console.log(`[togglebutton]: Adding toggle buttons to ${itemsToToggle.length} items`) + // Add the button to each admonition and hook up a callback to toggle visibility + itemsToToggle.forEach((item, index) => { + if (item.classList.contains("admonition")) { + // If it's an admonition block, then we'll add a button inside + // Generate unique IDs for this item + var toggleID = `toggle-${index}`; + var buttonID = `button-${toggleID}`; + + item.setAttribute('id', toggleID); + if (!item.classList.contains("toggle")){ + item.classList.add("toggle"); + } + // This is the button that will be added to each item to trigger the toggle + var collapseButton = ` + `; + + title = item.querySelector(".admonition-title") + title.insertAdjacentHTML("beforeend", collapseButton); + thisButton = document.getElementById(buttonID); + + // Add click handlers for the button + admonition title (if admonition) + admonitionTitle = document.querySelector(`#${toggleID} > .admonition-title`) + if (admonitionTitle) { + // If an admonition, then make the whole title block clickable + admonitionTitle.addEventListener('click', toggleClickHandler); + admonitionTitle.dataset.target = toggleID + admonitionTitle.dataset.button = buttonID + } else { + // If not an admonition then we'll listen for the button click + thisButton.addEventListener('click', toggleClickHandler); + } + + // Now hide the item for this toggle button unless explicitly noted to show + if (!item.classList.contains("toggle-shown")) { + toggleHidden(thisButton); + } + } else { + // If not an admonition, wrap the block in a
block + // Define the structure of the details block and insert it as a sibling + var detailsBlock = ` +
+ + ${toggleChevron} + ${toggleHintShow} + +
`; + item.insertAdjacentHTML("beforebegin", detailsBlock); + + // Now move the toggle-able content inside of the details block + details = item.previousElementSibling + details.appendChild(item) + item.classList.add("toggle-details__container") + + // Set up a click trigger to change the text as needed + details.addEventListener('click', (click) => { + let parent = click.target.parentElement; + if (parent.tagName.toLowerCase() == "details") { + summary = parent.querySelector("summary"); + details = parent; + } else { + summary = parent; + details = parent.parentElement; + } + // Update the inner text for the proper hint + if (details.open) { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintShow; + } else { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintHide; + } + + }); + + // If we have a toggle-shown class, open details block should be open + if (item.classList.contains("toggle-shown")) { + details.click(); + } + } + }) +}; + +// This should simply add / remove the collapsed class and change the button text +var toggleHidden = (button) => { + target = button.dataset['target'] + var itemToToggle = document.getElementById(target); + if (itemToToggle.classList.contains("toggle-hidden")) { + itemToToggle.classList.remove("toggle-hidden"); + button.classList.remove("toggle-button-hidden"); + } else { + itemToToggle.classList.add("toggle-hidden"); + button.classList.add("toggle-button-hidden"); + } +} + +var toggleClickHandler = (click) => { + // Be cause the admonition title is clickable and extends to the whole admonition + // We only look for a click event on this title to trigger the toggle. + + if (click.target.classList.contains("admonition-title")) { + button = click.target.querySelector(".toggle-button"); + } else if (click.target.classList.contains("tb-icon")) { + // We've clicked the icon and need to search up one parent for the button + button = click.target.parentElement; + } else if (click.target.tagName == "polyline") { + // We've clicked the SVG elements inside the button, need to up 2 layers + button = click.target.parentElement.parentElement; + } else if (click.target.classList.contains("toggle-button")) { + // We've clicked the button itself and so don't need to do anything + button = click.target; + } else { + console.log(`[togglebutton]: Couldn't find button for ${click.target}`) + } + target = document.getElementById(button.dataset['button']); + toggleHidden(target); +} + +// If we want to blanket-add toggle classes to certain cells +var addToggleToSelector = () => { + const selector = ""; + if (selector.length > 0) { + document.querySelectorAll(selector).forEach((item) => { + item.classList.add("toggle"); + }) + } +} + +// Helper function to run when the DOM is finished +const sphinxToggleRunWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} +sphinxToggleRunWhenDOMLoaded(addToggleToSelector) +sphinxToggleRunWhenDOMLoaded(initToggleItems) + +/** Toggle details blocks to be open when printing */ +if (toggleOpenOnPrint == "true") { + window.addEventListener("beforeprint", () => { + // Open the details + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.dataset["togglestatus"] = el.open; + el.open = true; + }); + + // Open the admonitions + document.querySelectorAll(".admonition.toggle.toggle-hidden").forEach((el) => { + console.log(el); + el.querySelector("button.toggle-button").click(); + el.dataset["toggle_after_print"] = "true"; + }); + }); + window.addEventListener("afterprint", () => { + // Re-close the details that were closed + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.open = el.dataset["togglestatus"] == "true"; + delete el.dataset["togglestatus"]; + }); + + // Re-close the admonition toggle buttons + document.querySelectorAll(".admonition.toggle").forEach((el) => { + if (el.dataset["toggle_after_print"] == "true") { + el.querySelector("button.toggle-button").click(); + delete el.dataset["toggle_after_print"]; + } + }); + }); +} diff --git a/branch/main/collaboration/code-review/index.html b/branch/main/collaboration/code-review/index.html new file mode 100644 index 0000000..2db66b2 --- /dev/null +++ b/branch/main/collaboration/code-review/index.html @@ -0,0 +1,289 @@ + + + + + + + Practicing code review — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Practicing code review

+

In this episode we will practice the code review process. We will learn how to +ask for changes in a pull request, how to suggest a change in a pull request, +and how to modify a pull request.

+

This will enable research groups to work more collaboratively and to not only +improve the code quality but also to learn from each other.

+
+

Exercise

+
+

Exercise preparation

+

We can continue in the same exercise repository which we have used in the +previous episode.

+
+
+

Exercise: Practicing code review (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • As a reviewer, we will learn how to ask for changes in a pull request.

  • +
  • As a reviewer, we will learn how to suggest a change in a pull request.

  • +
  • As a submitter, we will learn how to modify a pull request without closing +the incomplete one and opening a new one.

  • +
+

Exercise tasks:

+
    +
  1. Create a new branch and one or few commits: in these improve something but also +deliberately introduce a typo and also a larger mistake which we will want to fix during the code review.

  2. +
  3. Open a pull request towards the main branch.

  4. +
  5. As a reviewer to somebody else’s pull request, ask for an improvement and +also directly suggest a change for the small typo. (Hint: +suggestions are possible through the GitHub web interface, view of +a pull request, “Files changed” view, after selecting some lines. +Look for the “±” button.)

  6. +
  7. As the submitter, learn how to accept the suggested change. (Hint: +GitHub web interface, “Files Changed” view.)

  8. +
  9. As the submitter, improve the pull request without having to close and open +a new one: by adding a new commit to the same branch. (Hint: push +to the branch again.)

  10. +
  11. Once the changes are addressed, merge the pull request.

  12. +
+
+
+
+

Help and discussion

+

From here on out, we don’t give detailed steps to the solution. You +need to combine what you know, and the extra info below, in order to +solve the above.

+
+

How to ask for changes in a pull request

+

Technically, there are at least two common ways to ask for changes in a pull +request.

+

Either in the comment field of the pull request:

+
+Screenshot of a pull request comment field +
+

Or by using the “Review changes”:

+
+Screenshot of a pull request navigating to the "Review changes" tab +
+

And always please be kind and constructive in your comments. Remember that the +goal is not gate-keeping but collaborative learning.

+
+
+

How to suggest a change in a pull request as a reviewer

+

If you see a very small problem that is easy to fix, you can suggest a change +as a reviewer.

+

Instead of asking the submitter to tiny problem, you can suggest a change by +clicking on the plus sign next to the line number in the “Files changed” tab:

+
+Screenshot of leaving a comment to a line in a pull request +
+

Here you can comment on specific lines or even line ranges.

+

But now the interesting part is to click on the “Add a suggestion” symbol (the +one that looks like plus and minus). Now you can fix the tiny problem (in this +case a typo) and then click on the “Add single comment” button:

+
+Sequence of clicks to add a suggestion to a line in a pull request +
+

The result is this and the submitter can accept the change with a single click:

+
+Screenshot of a pull request with a suggested change +
+

After accepting with “Commit suggestion”, the improvement gets added to the +pull request.

+
+
+

How to modify a pull request to address the review comments

+

If the reviewer asks for changes, it is not necessary to close the pull request +and later open a new one. It can even be counter-productive to do so: This can +fragment the discussion and the history of the pull request and can make it +harder to understand the context of the changes.

+

A much better mechanism to recognize that pull requests are not implemented +from a specific commit to a specific branch, but always from a branch to a +branch.

+

This means that you can make amendments to the pull request by adding new +commits to the same source branch. This way the pull request will be updated +automatically and the reviewer can see the new changes and comment on them.

+

The fact that pull requests are from branch to branch also strongly suggests +that it is a good practice to create a new branch for each pull request. +Otherwise you could accidentally modify an open pull request by adding new +commits to the source branch.

+
+
+
+

Summary

+
    +
  • Our process isn’t just about code now. It’s about discussion and +working together to make the whole process better.

  • +
  • GitHub discussions and reviewing are quite powerful and can make +small changes easy.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/collaboration/concepts/index.html b/branch/main/collaboration/concepts/index.html new file mode 100644 index 0000000..f4c073d --- /dev/null +++ b/branch/main/collaboration/concepts/index.html @@ -0,0 +1,234 @@ + + + + + + + Concepts around collaboration — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Concepts around collaboration

+
+

Objectives

+
    +
  • Be able to decide whether to divide work at the branch level or at the repository level.

  • +
+
+
+

Commits, branches, repositories, forks, clones

+
    +
  • repository: The project, contains all data and history (commits, branches, tags).

  • +
  • commit: Snapshot of the project, gets a unique identifier (e.g. c7f0e8bfc718be04525847fc7ac237f470add76e).

  • +
  • branch: Independent development line. The main development line is often called main.

  • +
  • tag: A pointer to one commit, to be able to refer to it later. Like a commemorative plaque +that you attach to a particular commit (e.g. phd-printed or paper-submitted).

  • +
  • cloning: Copying the whole repository to your laptop - the first time. It is not necessary to download each file one by one.

  • +
  • forking: Taking a copy of a repository (which is typically not yours) - your +copy (fork) stays on GitHub/GitLab and you can make changes to your copy.

  • +
+
+
+

Cloning a repository

+

In order to make a complete copy a whole repository, the git clone command +can be used. When cloning, all the files, of all or selected branches, of a +repository are copied in one operation. Cloning of a repository is of relevance +in a few different situations:

+
    +
  • Working on your own, cloning is the operation that you can use to create +multiple instances of a repository on, for instance, a personal computer, a +server, and a supercomputer.

  • +
  • The parent repository could be a repository that you or your colleague own. A +common use case for cloning is when working together within a smaller team +where everyone has read and write access to the same git repository.

  • +
  • Alternatively, cloning can be made from a public repository of a code that +you would like to use. Perhaps you have no intention to work on the code, but +would like to stay in tune with the latest developments, also in-between +releases of new versions of the code.

  • +
+
+Forking and cloning +
+

Forking and cloning

+
+
+
+
+

Forking a repository

+

When a fork is made on GitHub/GitLab a complete copy, of all or selected +branches, of the repository is made. The copy will reside under a different +account on GitHub/GitLab. Forking of a repository is of high relevance when +working with a git repository to which you do not have write access.

+
    +
  • In the fork repository commits can be made to the base branch (main or +master), and to other branches.

  • +
  • The commits that are made within the branches of the fork repository can be +contributed back to the parent repository by means of pull or merge requests.

  • +
+
+
+

Synchronizing changes between repositories

+
    +
  • We need a mechanism to communicate changes between the repositories.

  • +
  • We will pull or fetch updates from remote repositories (we will soon discuss the difference between pull and fetch).

  • +
  • We will push updates to remote repositories.

  • +
  • We will learn how to suggest changes within repositories on GitHub and across repositories (pull request).

  • +
  • Repositories that are forked or cloned do not automatically synchronize themselves: +We will learn how to update forks (by pulling from the “central” repository).

  • +
  • A main difference between cloning a repository and forking a repository is that the former is a general operation for generating copies of a repository to different computers, whereas forking is a particular operation implemented on GitHub/GitLab.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/collaboration/demo-discussion/index.html b/branch/main/collaboration/demo-discussion/index.html new file mode 100644 index 0000000..405df1b --- /dev/null +++ b/branch/main/collaboration/demo-discussion/index.html @@ -0,0 +1,160 @@ + + + + + + + Conclict resolution, rebasing, and organizational strategies — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branch/main/collaboration/forking-workflow/index.html b/branch/main/collaboration/forking-workflow/index.html new file mode 100644 index 0000000..ed602bb --- /dev/null +++ b/branch/main/collaboration/forking-workflow/index.html @@ -0,0 +1,376 @@ + + + + + + + How to contribute changes to repositories that belong to others — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

How to contribute changes to repositories that belong to others

+

In this episode we prepare you to suggest and contribute changes to +repositories that belong to others. These might be open source projects that +you use in your work.

+

We will see how Git and services like GitHub or GitLab can be used to suggest +modification without having to ask for write access to the repository and +accept modifications without having to grant write access to others.

+
+

Exercise

+
+

Exercise preparation

+
    +
  • The exercise repository is now different: +https://github.com/workshop-material/recipe-book-forking-exercise (note the -forking-exercise).

  • +
  • First fork the exercise repository to your GitHub account.

  • +
  • Then clone your fork to your computer (if you wish to work locally).

  • +
  • Double-check that you have forked the correct repository.

  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Forking a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • Opening a pull request towards the upstream repository.

  • +
  • Pull requests can be coupled with automated testing.

  • +
  • Learning that your fork can get out of date.

  • +
  • After the pull requests are merged, updating your fork with the changes.

  • +
  • Learn how to approach other people’s repositories with ideas, changes, and requests.

  • +
+

Exercise tasks:

+
    +
  1. Open an issue in the upstream exercise repository where you describe the +change you want to make. Take note of the issue number.

  2. +
  3. Create a new branch in your fork of the repository.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit cross-reference the issue you opened. +See the walk-through below for how to do this.

  6. +
  7. Open a pull request towards the upstream repository.

  8. +
  9. The instructor will review and merge the pull requests. +During the review, pay attention to the automated test step (here for +demonstration purposes, we test whether the recipe contains an ingredients +and an instructions sections).

  10. +
  11. After few pull requests are merged, update your fork with the changes.

  12. +
  13. Check that in your fork you can see changes from other people’s pull requests.

  14. +
+
+
+
+

Help and discussion

+
+

Help! I don’t have permissions to push my local changes

+

Maybe you see an error like this one:

+
Please make sure you have the correct access rights
+and the repository exists.
+
+
+

Or like this one:

+
failed to push some refs to workshop-material/recipe-book-forking-exercise.git
+
+
+

In this case you probably try to push the changes not to your fork but to the original repository +and in this exercise you do not have write access to the original repository.

+

The simpler solution is to clone again but this time your fork.

+ +
+
+

Opening a pull request towards the upstream repository

+

We have learned in the previous episode that pull requests are always from +branch to branch. But the branch can be in a different repository.

+

When you open a pull request in a fork, by default GitHub will suggest to +direct it towards the default branch of the upstream repository.

+

This can be changed and it should always be verified, but in this case this is +exactly what we want to do, from fork towards upstream:

+
+Screenshot of a pull request from fork towards upstream +
+
+
+

Pull requests can be coupled with automated testing

+

We added an automated test here just for fun and so that you see that this is +possible to do.

+

In this exercise, the test is silly. It will check whether the recipe contains +both an ingredients and an instructions section.

+

In this example the test failed:

+
+Screenshot of a failed test in a pull request +
+

Click on the “Details” link to see the details of the failed test:

+
+Screenshot of details why the test failed +
+

How can this be useful?

+
    +
  • The project can define what kind of tests are expected to pass before a pull +request can be merged.

  • +
  • The reviewer can see the results of the tests, without having to run them +locally.

  • +
+

How does it work?

+
    +
  • We added a GitHub Actions workflow +to automatically run on each push or pull request towards the main branch.

  • +
+

What tests or steps can you image for your project to run automatically with +each pull request?

+
+
+

How to update your fork with changes from upstream

+

This used to be difficult but now it is two mouse clicks.

+

Navigate to your fork and notice how GitHub tells you that your fork is behind. +In my case, it is 9 commits behind upstream. To fix this, click on “Sync fork” +and then “Update branch”:

+
+Screenshot on GitHub fork page showing that the fork is behind +
+

After the update my “branch is up to date” with the upstream repository:

+
+Screenshot on GitHub after fork has been updated +
+
+
+

How to approach other people’s repositories with ideas, changes, and requests

+

Contributing very minor changes

+
    +
  • Clone or fork+clone repository

  • +
  • Create a branch

  • +
  • Commit and push change

  • +
  • Open a pull request or merge request

  • +
+

If you observe an issue and have an idea how to fix it

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • Describe the problem

  • +
  • If you have a suggestion on how to fix it, describe your suggestion

  • +
  • Possibly discuss and get feedback

  • +
  • If you are working on the fix, indicate it in the issue so that others know that somebody is working on it and who is working on it

  • +
  • Submit your fix as pull request or merge request which references/closes the issue

  • +
+
+

Motivation

+
    +
  • Inform others about an observed problem

  • +
  • Make it clear whether this issue is up for grabs or already being worked on

  • +
+
+

If you have an idea for a new feature

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • In the issue, write a short proposal for your suggested change or new feature

  • +
  • Motivate why and how you wish to do this

  • +
  • Also indicate where you are unsure and where you would like feedback

  • +
  • Discuss and get feedback before you code

  • +
  • Once you start coding, indicate that you are working on it

  • +
  • Once you are done, submit your new feature as pull request or merge request which references/closes the issue/proposal

  • +
+
+

Motivation

+
    +
  • Get agreement and feedback before writing 5000 lines of code which might be rejected

  • +
  • If we later wonder why something was done, we have the issue/proposal as +reference and can read up on the reasoning behind a code change

  • +
+
+
+
+
+

Summary

+
    +
  • This forking workflow lets you propose changes to repositories for +which you have no write access.

  • +
  • This is the way that much modern open-source software works.

  • +
  • You can now contribute to any project you can view.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/collaboration/index.html b/branch/main/collaboration/index.html new file mode 100644 index 0000000..975bbbe --- /dev/null +++ b/branch/main/collaboration/index.html @@ -0,0 +1,167 @@ + + + + + + + Collaborative version control and code review — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branch/main/collaboration/same-repository/index.html b/branch/main/collaboration/same-repository/index.html new file mode 100644 index 0000000..e901473 --- /dev/null +++ b/branch/main/collaboration/same-repository/index.html @@ -0,0 +1,423 @@ + + + + + + + Collaborating within the same repository — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Collaborating within the same repository

+

In this episode, we will learn how to collaborate within the same repository. +We will learn how to cross-reference issues and pull requests, how to review +pull requests, and how to use draft pull requests.

+

This exercise will form a good basis for collaboration that is suitable for +most research groups.

+
+

Note

+

When you read or hear pull request, please think of a change proposal.

+
+
+

Exercise

+

In this exercise, we will contribute to a repository via a pull request. +This means that you propose some change, and then it is accepted (or not).

+
+

Exercise preparation

+
    +
  • First we need to get access to the exercise repository to which we will +contribute.

    +
      +
    • Instructor collects GitHub usernames from learners and adds them as collaborators to the exercise repository +(Settings -> Collaborators and teams -> Manage access -> Add people).

    • +
    +
  • +
  • Don’t forget to accept the invitation

    +
      +
    • Check https://github.com/settings/organizations/

    • +
    • Alternatively check the inbox for the email account you registered with +GitHub. GitHub emails you an invitation link, but if you don’t receive it +you can go to your GitHub notifications in the top right corner. The +maintainer can also “copy invite link” and share it within the group.

    • +
    +
  • +
  • Watching and unwatching repositories

    +
      +
    • Now that you are a collaborator, you get notified about new issues and pull +requests via email.

    • +
    • If you do not wish this, you can “unwatch” a repository (top of +the project page).

    • +
    • However, we recommend watching repositories you are interested +in. You can learn things from experts just by watching the +activity that come through a popular project.

    • +
    +
    +Unwatching a repository +
    +

    Unwatch a repository by clicking “Unwatch” in the repository view, +then “Participating and @mentions” - this way, you will get +notifications about your own interactions.

    +
    +
    +
  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements (from installation instructions):

+ +

What is familiar from the previous workshop day (not repeated here):

+
    +
  • Cloning a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Submit a pull request towards the main branch.

  • +
+

What will be new in this exercise:

+
    +
  • If you create the changes locally, you will need to push them to the remote repository.

  • +
  • Learning what a protected branch is and how to modify a protected branch: using a pull request.

  • +
  • Cross-referencing issues and pull requests.

  • +
  • Practice to review a pull request.

  • +
  • Learn about the value of draft pull requests.

  • +
+

Exercise tasks:

+
    +
  1. Start in the exercise +repository and open an +issue where you describe the change you want to make. Note down the issue +number since you will need it later.

  2. +
  3. Create a new branch.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit +cross-reference the issue you opened (see the walk-through below +for how to do that).

  6. +
  7. Push your new branch (with the new commit) to the repository you +are working on.

  8. +
  9. Open a pull request towards the main branch.

  10. +
  11. Review somebody else’s pull request and give constructive feedback. Merge their pull request.

  12. +
  13. Try to create a new branch with some half-finished work and open a draft +pull request. Verify that the draft pull request cannot be merged since it +is not meant to be merged yet.

  14. +
+
+
+
+

Solution and hints

+
+

(1) Opening an issue

+

This is done through the GitHub web interface. For example, you could +give the name of the recipe you want to add (so that others don’t add +the same one). It is the “Issues” tab.

+
+
+

(2) Create a new branch.

+

If on GitHub, you can make the branch in the web interface.

+
+
+

(3) Make a change adding the recipe

+

Add a new file with the recipe in it. Commit the file. In the commit +message, include the note about the issue number, saying that this +will close that issue.

+
+

Cross-referencing issues and pull requests

+

Each issue and each pull request gets a number and you can cross-reference them.

+

When you open an issue, note down the issue number (in this case it is #2):

+
+Each issue gets a number +
+

You can reference this issue number in a commit message or in a pull request, like in this +commit message:

+
this is the new recipe; fixes #2
+
+
+

If you forget to do that in your commit message, you can also reference the issue +in the pull request description. And instead of fixes you can also use closes or resolves +or fix or close or resolve (case insensitive).

+

Here are all the keywords that GitHub recognizes: +https://help.github.com/en/articles/closing-issues-using-keywords

+

Then observe what happens in the issue once your commit gets merged: it will +automatically close the issue and create a link between the issue and the +commit. This is very useful for tracking what changes were made in response to +which issue and to know from when until when precisely the issue was open.

+
+
+
+

(4) Push to GitHub as a new branch

+

Push the branch to the repository. You should end up with a branch +visible in the GitHub web view.

+

This is only necessary if you created the changes locally. If you created the +changes directly on GitHub, you can skip this step.

+
+

In VS Code, you can “publish the branch” to the remote repository by clicking +the cloud icon in the bottom left corner of the window:

+
+Publishing a branch in VS Code +
+
+
+
+

(5) Open a pull request towards the main branch

+

This is done through the GitHub web interface.

+
+
+

(6) Reviewing pull requests

+

You review through the GitHub web interface.

+

Checklist for reviewing a pull request:

+
    +
  • Be kind, on the other side is a human who has put effort into this.

  • +
  • Be constructive: if you see a problem, suggest a solution.

  • +
  • Towards which branch is this directed?

  • +
  • Is the title descriptive?

  • +
  • Is the description informative?

  • +
  • Scroll down to see commits.

  • +
  • Scroll down to see the changes.

  • +
  • If you get incredibly many changes, also consider the license or copyright +and ask where all that code is coming from.

  • +
  • Again, be kind and constructive.

  • +
  • Later we will learn how to suggest changes directly in the pull request.

  • +
+

If someone is new, it’s often nice to say something encouraging in the +comments before merging (even if it’s just “thanks”). If all is good +and there’s not much else to say, you could merge directly.

+
+
+

(7) Draft pull requests

+

Try to create a draft pull request:

+
+Creating a draft pull request +
+

Verify that the draft pull request cannot be merged until it is marked as ready +for review:

+
+Draft pull request cannot be merged +
+

Draft pull requests can be useful for:

+
    +
  • Feedback: You can open a pull request early to get feedback on your work without +signaling that it is ready to merge.

  • +
  • Information: They can help communicating to others that a change is coming up and in +progress.

  • +
+
+
+

What is a protected branch? And how to modify it?

+

A protected branch on GitHub or GitLab is a branch that cannot (accidentally) +deleted or force-pushed to. It is also possible to require that a branch cannot +be directly pushed to or modified, but that changes must be submitted via a +pull request.

+

To protect a branch in your own repository, go to “Settings” -> “Branches”.

+
+
+

Summary

+
    +
  • We used all the same pieces that we’ve learned previously.

  • +
  • But we successfully contributed to a collaborative project!

  • +
  • The pull request allowed us to contribute without changing directly: +this is very good when it’s not mainly our project.

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/credit/index.html b/branch/main/credit/index.html new file mode 100644 index 0000000..e4d14e5 --- /dev/null +++ b/branch/main/credit/index.html @@ -0,0 +1,158 @@ + + + + + + + Credit — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branch/main/documentation/index.html b/branch/main/documentation/index.html new file mode 100644 index 0000000..74b8b75 --- /dev/null +++ b/branch/main/documentation/index.html @@ -0,0 +1,540 @@ + + + + + + + Code documentation — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Code documentation

+
+

Objectives

+
    +
  • Discuss what makes good documentation.

  • +
  • Improve the README of your project or our example project.

  • +
  • Explore Sphinx which is a popular tool to build documentation websites.

  • +
  • Learn how to leverage GitHub Actions and GitHub Pages to build and deploy documentation.

  • +
+
+
+

Instructor note

+
    +
  • (30 min) Discussion

  • +
  • (30 min) Exercise: Set up a Sphinx documentation and add API documentation

  • +
  • (15 min) Demo: Building documentation with GitHub Actions

  • +
+
+
+

Why? 💗✉️ to your future self

+
    +
  • You will probably use your code in the future and may forget details.

  • +
  • You may want others to use your code or contribute +(almost impossible without documentation).

  • +
+
+
+

In-code documentation

+

Not very useful (more commentary than comment):

+
# now we check if temperature is below -50
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

More useful (explaining why):

+
# we regard temperatures below -50 degrees as measurement errors
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

Keeping zombie code “just in case” (rather use version control):

+
# do not run this code!
+# if temperature > 0:
+#     print("It is warm")
+
+
+

Emulating version control:

+
# John Doe: threshold changed from 0 to 15 on August 5, 2013
+if temperature > 15:
+    print("It is warm")
+
+
+
+
+

Many languages allow “docstrings”

+

Example (Python):

+
def kelvin_to_celsius(temp_k: float) -> float:
+    """
+    Converts temperature in Kelvin to Celsius.
+
+    Parameters
+    ----------
+    temp_k : float
+        temperature in Kelvin
+
+    Returns
+    -------
+    temp_c : float
+        temperature in Celsius
+    """
+    assert temp_k >= 0.0, "ERROR: negative T_K"
+
+    temp_c = temp_k - 273.15
+
+    return temp_c
+
+
+
+

Keypoints

+
    +
  • Documentation which is only in the source code is not enough.

  • +
  • Often a README is enough.

  • +
  • Documentation needs to be kept +in the same Git repository as the code since we want it to evolve with +the code.

  • +
+
+
+
+

Often a README is enough - checklist

+
    +
  • Purpose

  • +
  • Requirements

  • +
  • Installation instructions

  • +
  • Copy-paste-able example to get started

  • +
  • Tutorials covering key functionality

  • +
  • Reference documentation (e.g. API) covering all functionality

  • +
  • Authors and recommended citation

  • +
  • License

  • +
  • Contribution guide

  • +
+

See also the +JOSS review checklist.

+
+
+

What if you need more than a README?

+ +
+
+

Diátaxis

+

Diátaxis is a systematic approach to technical documentation authoring.

+ +
+
+

Exercise: Set up a Sphinx documentation

+
+

Preparation

+

In this episode we will use the following 5 packages which we installed +previously as part of the Conda environment or Virtual environment:

+
myst-parser
+sphinx
+sphinx-rtd-theme
+sphinx-autoapi
+sphinx-autobuild
+
+
+

Which repository to use? You have 3 options:

+
    +
  • Clone your fork of the planets example repository.

  • +
  • If you don’t have that, you can clone the original repository itself: +https://github.com/workshop-material/planets

  • +
  • You can try this with your own project and the project does not have to +be a Python project.

  • +
+
+

There are at least two ways to get started with Sphinx:

+
    +
  1. Use sphinx-quickstart to create a new Sphinx project.

  2. +
  3. This is what we will do: Create three files (doc/conf.py, doc/index.md, and doc/about.md) +as starting point and improve from there.

  4. +
+
+

Exercise: Set up a Sphinx documentation

+
    +
  1. Create the following three files in your project:

    +
    planets/
    +├── doc/
    +│   ├── conf.py
    +│   ├── index.md
    +│   └── about.md
    +└── ...
    +
    +
    +

    This is conf.py:

    +
    project = "planets"
    +copyright = "2024, Authors"
    +author = "Authors"
    +release = "0.1"
    +
    +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
    +
    +extensions = [
    +    "myst_parser",  # in order to use markdown
    +]
    +
    +myst_enable_extensions = [
    +    "colon_fence",  # ::: can be used instead of ``` for better rendering
    +]
    +
    +html_theme = "sphinx_rtd_theme"
    +
    +
    +

    This is index.md (feel free to adjust):

    +
    # Our code documentation
    +
    +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
    +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
    +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
    +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
    +culpa qui officia deserunt mollit anim id est laborum.
    +
    +:::{toctree}
    +:maxdepth: 2
    +:caption: Some caption
    +
    +about.md
    +:::
    +
    +
    +

    This is about.md (feel free to adjust):

    +
    # About this code
    +
    +Work in progress ...
    +
    +
    +
  2. +
  3. Run sphinx-build to build the HTML documentation:

    +
    $ sphinx-build doc _build
    +
    +... lots of output ...
    +The HTML pages are in _build.
    +
    +
    +
  4. +
  5. Try to open _build/index.html in your browser.

  6. +
  7. Experiment with adding more content, images, equations, code blocks, …

    + +
  8. +
+
+

There is a lot more you can do:

+
    +
  • This is useful if you want to check the integrity of all internal and external links:

    +
    $ sphinx-build doc -W -b linkcheck _build
    +
    +
    +
  • +
  • sphinx-autobuild +provides a local web server that will automatically refresh your view +every time you save a file - which makes writing and testing much easier.

  • +
+
+
+

Demo: Building documentation with GitHub Actions

+

First we need to extend the environment.yml file to include the necessary packages:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - myst-parser
+  - sphinx
+  - sphinx-rtd-theme
+  - sphinx-autoapi
+
+
+

Then we add a GitHub Actions workflow .github/workflow/sphinx.yml to build the documentation:

+
name: Build documentation
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+permissions:
+  contents: write
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Sphinx build
+      run: |
+        sphinx-build doc _build
+      shell: bash -el {0}
+
+    - name: Deploy to GitHub Pages
+      uses: peaceiris/actions-gh-pages@v4
+      if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+      with:
+        publish_branch: gh-pages
+        github_token: ${{ secrets.GITHUB_TOKEN }}
+        publish_dir: _build/
+        force_orphan: true
+
+
+

Now:

+
    +
  • Add these two changes to the GitHub repository.

  • +
  • Go to “Settings” -> “Pages” -> “Branch” -> gh-pages -> “Save”.

  • +
  • Look at “Actions” tab and observe the workflow running and hopefully +deploying the website.

  • +
  • Finally visit the generated site. You find it by clicking the About wheel +icon on top right of your repository. There, select “Use your GitHub Pages +website”.

  • +
  • This is how we build almost all of our lesson websites (including this one).

  • +
  • Another popular place to deploy Sphinx documentation is ReadTheDocs.

  • +
+
+
+

Optional: How to auto-generate API documentation in Python

+

Add three tiny modifications (highlighted) to doc/conf.py to auto-generate API documentation +(this requires the sphinx-autoapi package):

+
project = "planets"
+copyright = "2024, Authors"
+author = "Authors"
+release = "0.1"
+
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+extensions = [
+    "myst_parser",  # in order to use markdown
+    "autoapi.extension",  # in order to use markdown
+]
+
+# search this directory for Python files
+autoapi_dirs = [".."]
+
+# ignore this file when generating API documentation
+autoapi_ignore = ["*/conf.py"]
+
+myst_enable_extensions = [
+    "colon_fence",  # ::: can be used instead of ``` for better rendering
+]
+
+html_theme = "sphinx_rtd_theme"
+
+
+

Then rebuild the documentation (or push the changes and let GitHub rebuild it) +and you should see a new section “API Reference”.

+
+
+

Confused about reStructuredText vs. Markdown vs. MyST?

+
    +
  • At the beginning there was reStructuredText and Sphinx was built for reStructuredText.

  • +
  • Independently, Markdown was invented and evolved into a couple of flavors.

  • +
  • Markdown became more and more popular but was limited compared to reStructuredText.

  • +
  • Later, MyST +was invented to be able to write +something that looks like Markdown but in addition can do everything that +reStructuredText can do with extra directives.

  • +
+
+
+

Where to read more

+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/example/index.html b/branch/main/example/index.html new file mode 100644 index 0000000..c0c506a --- /dev/null +++ b/branch/main/example/index.html @@ -0,0 +1,237 @@ + + + + + + + Example project: Simulating the motion of planets — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Example project: Simulating the motion of planets

+

The example code that we will study +is a hopefully simple N-body simulation written in Python. It is not important +or expected that we understand the code in any detail.

+

The big picture is that the code simulates the motion of a number of +planets:

+
    +
  • We can choose the number of planets.

  • +
  • Each planet starts with a random position, velocity, and mass.

  • +
  • At each time step, the code calculates the gravitational force between each +pair of planets.

  • +
  • The forces accelerate each planet, the acceleration modifies the velocity, +the velocity modifies the position of each planet.

  • +
  • We can choose the number of time steps.

  • +
  • The units were chosen to make numbers easy to read.

  • +
+
+

Example run

+
+

Instructor note

+

The instructor demonstrates running the code on their computer.

+
+

The code is written to accept command-line arguments to specify the number +of planets and the number of time steps.

+

We first generate starting data:

+
$ python generate-data.py --num-planets 10 --output-file initial.csv
+
+
+

The generated file (initial.csv) could look like this:

+
px,py,pz,vx,vy,vz,mass
+-46.88,-42.51,88.33,-0.86,-0.18,0.55,6.70
+-5.29,17.09,-96.13,0.66,0.45,-0.17,3.51
+83.53,-92.83,-68.77,-0.26,-0.48,0.24,6.84
+-36.31,25.48,64.16,0.85,0.75,-0.56,1.53
+-68.38,-17.21,-97.07,0.60,0.26,0.69,6.63
+-48.37,-48.74,3.92,-0.92,-0.33,-0.93,8.60
+40.53,-75.50,44.18,-0.62,-0.31,-0.53,8.04
+-27.21,10.78,-78.82,-0.09,-0.55,-0.03,5.35
+88.42,-74.95,-45.85,0.81,0.68,0.56,5.36
+39.09,53.12,-59.54,-0.54,0.56,0.07,8.98
+
+
+

Then we can simulate their motion (in this case for 20 steps):

+
$ python simulate.py --num-steps 20 \
+                     --input-file initial.csv \
+                     --output-file final.csv
+
+
+

The --output-file (final.csv) is again a CSV file (comma-separated values) +and contains the final positions of all planets.

+

It is possible to run on multiple cores and to animate the result. +Here is an example with 100 planets:

+
$ python generate-data.py --num-planets 100 --output-file initial.csv
+
+$ python simulate.py --num-steps 50 \
+                     --input-file initial.csv \
+                     --output-file final.csv \
+                     --trajectories-file trajectories.npz \
+                     --num-cores 8
+
+$ python animate.py --initial-file initial.csv \
+                    --trajectories-file trajectories.npz \
+                    --output-file animation.mp4
+
+
+
+

Learning goals

+
    +
  • What are the most important steps to make this code reusable by others +and our future selves?

  • +
  • Be able to apply these techniques to your own code/script.

  • +
+
+
+

We will not focus on …

+
    +
  • … how the code works internally in detail.

  • +
  • … whether this is the most efficient algorithm.

  • +
  • … whether the code is numerically stable.

  • +
  • … how to code scales with the number of cores.

  • +
  • … whether it is portable to other operating systems (we will discuss this later).

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/genindex/index.html b/branch/main/genindex/index.html new file mode 100644 index 0000000..99f0d30 --- /dev/null +++ b/branch/main/genindex/index.html @@ -0,0 +1,149 @@ + + + + + + Index — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branch/main/index.html b/branch/main/index.html new file mode 100644 index 0000000..a97289d --- /dev/null +++ b/branch/main/index.html @@ -0,0 +1,290 @@ + + + + + + + Reproducible research software development using Python — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Reproducible research software development using Python

+
+

Big-picture goal

+

This is a hands-on course on research software engineering. In this +workshop we assume that most workshop participants use Python in their work or +a leading a group which uses Python. Therefore, some of the examples will use +Python as the example language.

+

We will work with an example project and go through all the steps of a typical +software project. Once we have seen the building blocks, we will try to apply +them to own projects. Workshop participants will receive and also learn to give +constructive code feedback.

+
+
+

Prerequisites

+
+

Preparation

+
    +
  1. Get a GitHub account following these instructions.

  2. +
  3. You will need a text editor. If you don’t have a favorite one, we recommend +VS Code.

  4. +
  5. If you prefer to work in the terminal and not in VS Code, set up these two (skip this if you use VS Code):

    + +
  6. +
  7. One of these two software environments (if you are not sure which one to +choose or have no preference, choose Conda):

    + +
  8. +
  9. Optional and only on Linux: Apptainer following +these instructions.

  10. +
+
+
+
+

Schedule

+
+

Day 1

+ +
+
+

Day 2

+ +
+
+

Day 3

+ +
+
+

If we have time left

+ +
+
+
+
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/installation/conda/index.html b/branch/main/installation/conda/index.html new file mode 100644 index 0000000..70fe17a --- /dev/null +++ b/branch/main/installation/conda/index.html @@ -0,0 +1,252 @@ + + + + + + + Conda environment — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Conda environment

+

A Conda environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a environment.yml file that documents the dependencies:

+
name: coderefinery
+channels:
+  - conda-forge
+  - bioconda
+dependencies:
+  - python >= 3.10
+  - black
+  - click
+  - flit
+  - ipywidgets
+  - isort
+  - jupyterlab
+  - jupyterlab_code_formatter
+  - jupyterlab-git
+  - matplotlib
+  - myst-parser
+  - nbdime
+  - numpy
+  - pandas
+  - pytest
+  - pytest-cov
+  - scalene
+  - seaborn
+  - snakemake-minimal
+  - sphinx
+  - sphinx-autoapi
+  - sphinx-autobuild
+  - sphinx_rtd_theme >= 2.0
+  - vulture
+  - scikit-image
+
+
+
+

Before you create a virtual environment

+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create an environment.yml file and copy-paste the dependencies above into it.

  4. +
+
+
+

Choose the tool to manage the environment

+

If you are already using one of these tools, please continue using the tool that you like and know. +If you are new to this, we recommend using Miniconda or Miniforge.

+
    +
  • Anaconda

    +
      +
    • Advantages: easy to install, easy to use, good for beginners

    • +
    • Disadvantages: large download, installs more than we will need, license restrictions

    • +
    +
  • +
  • Miniconda

    +
      +
    • Advantages: small size, installs only what you need

    • +
    • Disadvantages: no graphical interface, license restrictions

    • +
    +
  • +
  • Miniforge

    +
      +
    • Advantages: small size, no license restrictions

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Micromamba

    +
      +
    • Advantages: fast, small size

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Pixi

    +
      +
    • Advantages: fast and new

    • +
    • Disadvantages: new and less tested and not documented here

    • +
    +
  • +
+
+
+

Creating the virtual environment

+
    +
  1. Open your terminal shell (e.g. Bash or Zsh).

  2. +
  3. Activate conda using conda activate or source ~/miniconda3/bin/activate.

  4. +
  5. Run the following command:

    +
    $ conda env create --file environment.yml
    +
    +
    +
  6. +
  7. Make sure that you see “coderefinery” in the output when you ask for a list of all available environments:

    +
    $ conda env list
    +
    +
    +
  8. +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/installation/virtual-environment/index.html b/branch/main/installation/virtual-environment/index.html new file mode 100644 index 0000000..72439ff --- /dev/null +++ b/branch/main/installation/virtual-environment/index.html @@ -0,0 +1,217 @@ + + + + + + + Virtual environment — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Virtual environment

+

A virtual environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a requirements.txt file that documents the dependencies:

+
black
+click
+flit
+ipywidgets
+isort
+jupyterlab
+jupyterlab-code-formatter
+jupyterlab-git
+matplotlib
+myst-parser
+nbdime
+numpy
+pandas
+pytest
+pytest-cov
+scalene
+seaborn
+sphinx
+sphinx-autoapi
+sphinx-autobuild
+sphinx_rtd_theme >= 2.0
+vulture
+scikit-image
+
+
+
+

Before you create a virtual environment

+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create a requirements.txt file and copy-paste the dependencies above into it.

  4. +
+
+
+

Creating the virtual environment

+

Now create a virtual environment in this directory either using pip and +venv +(more traditional and safer) or using uv (more modern but also less tested):

+
+

Create a new virtual environment and activate it:

+
$ python3 -m venv coderefinery-environment
+$ source coderefinery-environment/bin/activate
+
+
+

Install the dependencies into the environment:

+
$ python3 -m pip install -r requirements.txt
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/lesson.pdf b/branch/main/lesson.pdf new file mode 100644 index 0000000..d032217 Binary files /dev/null and b/branch/main/lesson.pdf differ diff --git a/branch/main/objects.inv b/branch/main/objects.inv new file mode 100644 index 0000000..31aff9c Binary files /dev/null and b/branch/main/objects.inv differ diff --git a/branch/main/profiling/index.html b/branch/main/profiling/index.html new file mode 100644 index 0000000..ea98d96 --- /dev/null +++ b/branch/main/profiling/index.html @@ -0,0 +1,164 @@ + + + + + + + Profiling memory and CPU usage — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Profiling memory and CPU usage

+

Profilers:

+
    +
  • cProfile or profile

  • +
  • line_profiler

  • +
  • viztracer

  • +
  • scalene

  • +
  • Intel VTune

  • +
  • PyInstrument

  • +
  • py-spy

  • +
  • Pyflame

  • +
  • timeit

  • +
+

(here I will add some examples that we can profile together)

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/publishing/index.html b/branch/main/publishing/index.html new file mode 100644 index 0000000..24ea5b1 --- /dev/null +++ b/branch/main/publishing/index.html @@ -0,0 +1,161 @@ + + + + + + + How to release and publish your code — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

How to release and publish your code

+

Part 1:

+
    +
  • 30 min: Overview over software licenses and code publishing

  • +
  • 15 min: We add a license, CFF file, and publish to Zenodo (sandbox)

  • +
+

Part 2:

+
    +
  • 30 min: Creating a Python package with the help of flit

  • +
  • 15 min: Deploy the package to test-PyPI using GitHub Actions

  • +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/refactoring-concepts/index.html b/branch/main/refactoring-concepts/index.html new file mode 100644 index 0000000..ca7705e --- /dev/null +++ b/branch/main/refactoring-concepts/index.html @@ -0,0 +1,157 @@ + + + + + + + Concepts in refactoring and modular code design — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Concepts in refactoring and modular code design

+

Topics:

+
    +
  • Pure functions

  • +
  • Design patterns: functional design vs. object-oriented design

  • +
  • How to design your code before writing it: document-driven development

  • +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/refactoring-demo/index.html b/branch/main/refactoring-demo/index.html new file mode 100644 index 0000000..235a7cd --- /dev/null +++ b/branch/main/refactoring-demo/index.html @@ -0,0 +1,349 @@ + + + + + + + Demo: From a script towards a workflow — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Demo: From a script towards a workflow

+

In this episode we will explore code quality and good practices in Python using +a hands-on approach. We will together build up a small project and improve it +step by step.

+

We will start from a relatively simple image processing script which can read a +telescope image of stars and our goal is to count the number of stars in +the image. Later we will want to be able to process many such images.

+

The (fictional) telescope images look like the one below here (in this +repository we can find more):

+
+Generated image representing a telescope image of stars +
+

Generated image representing a telescope image of stars.

+
+
+
+

Rough plan for this demo

+
    +
  • (15 min) Discuss how we would solve the problem, run example code, and make it work (as part of a Jupyter notebook)?

  • +
  • (15 min) Refactor the positioning code into a function and a module

  • +
  • (15 min) Now we wish to process many images - discuss how we would approach this

  • +
  • (15 min) Introduce CLI and discuss the benefits

  • +
  • (30 min) From a script to a workflow (using Snakemake)

  • +
+
+ +
+

Plan

+

Topics we wish to show and discuss:

+
    +
  • Naming (and other) conventions, project organization, modularity

  • +
  • The value of pure functions and immutability

  • +
  • Refactoring (explained through examples)

  • +
  • Auto-formatting and linting with tools like black, vulture, ruff

  • +
  • Moving a project under Git

  • +
  • How to document dependencies

  • +
  • Structuring larger software projects in a modular way

  • +
  • Command-line interfaces

  • +
  • Workflows with Snakemake

  • +
+

We will work together on the code on the big screen, and participants will be +encouraged to give suggestions and ask questions. We will end up with a Git +repository which will be shared with workshop participants.

+
+
+

Possible solutions

+ + +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/reusable/index.html b/branch/main/reusable/index.html new file mode 100644 index 0000000..b17c103 --- /dev/null +++ b/branch/main/reusable/index.html @@ -0,0 +1,163 @@ + + + + + + + How to make the project more reusable — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

How to make the project more reusable

+

Part 1:

+
    +
  • 15 min: Motivation

  • +
  • 15 min: Best practices for managing dependencies in Python

  • +
  • 20 min: Exercise

  • +
+

Part 2:

+
    +
  • 10 min: Introduction to containers

  • +
  • 20 min: Demonstration

  • +
  • 10 min: Discussion

  • +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/search/index.html b/branch/main/search/index.html new file mode 100644 index 0000000..655dc57 --- /dev/null +++ b/branch/main/search/index.html @@ -0,0 +1,163 @@ + + + + + + Search — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branch/main/searchindex.js b/branch/main/searchindex.js new file mode 100644 index 0000000..abec3f0 --- /dev/null +++ b/branch/main/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"(1) Basic browsing": [[20, "basic-browsing"]], "(1) Create a new branch and a new commit": [[19, "create-a-new-branch-and-a-new-commit"]], "(1) Opening an issue": [[5, "opening-an-issue"]], "(2) Compare commit history with network graph": [[20, "compare-commit-history-with-network-graph"]], "(2) Create a new branch.": [[5, "create-a-new-branch"]], "(2) Modify the file again with a new commit": [[19, "modify-the-file-again-with-a-new-commit"]], "(3) How can you browse the history of a single file?": [[20, "how-can-you-browse-the-history-of-a-single-file"]], "(3) Make a change adding the recipe": [[5, "make-a-change-adding-the-recipe"]], "(3) Switch to the main branch and create a commit there": [[19, "switch-to-the-main-branch-and-create-a-commit-there"]], "(4) Browse the commits you just made": [[19, "browse-the-commits-you-just-made"]], "(4) Push to GitHub as a new branch": [[5, "push-to-github-as-a-new-branch"]], "(4) Which files include the word \u201cposition\u201d?": [[20, "which-files-include-the-word-position"]], "(5) Compare the branches": [[19, "compare-the-branches"]], "(5) Open a pull request towards the main branch": [[5, "open-a-pull-request-towards-the-main-branch"]], "(5) Who modified each line last and when?": [[20, "who-modified-each-line-last-and-when"]], "(6) Can you use this code yourself? Are you allowed to share modifications?": [[20, "can-you-use-this-code-yourself-are-you-allowed-to-share-modifications"]], "(6) Compare two arbitrary commits": [[19, "compare-two-arbitrary-commits"]], "(6) Reviewing pull requests": [[5, "reviewing-pull-requests"]], "(7) Draft pull requests": [[5, "draft-pull-requests"]], "(7) Renaming a branch": [[19, "renaming-a-branch"]], "(8) Creating a tag": [[19, "creating-a-tag"]], "Adding the unit test to GitHub Actions": [[17, "adding-the-unit-test-to-github-actions"]], "Automated testing": [[17, null]], "Background": [[19, "background"]], "Before you create a virtual environment": [[10, "before-you-create-a-virtual-environment"], [11, "before-you-create-a-virtual-environment"]], "Big-picture goal": [[9, "big-picture-goal"]], "Choose the tool to manage the environment": [[10, "choose-the-tool-to-manage-the-environment"]], "Cloning a repository": [[2, "cloning-a-repository"]], "Code documentation": [[7, null]], "Collaborating within the same repository": [[5, null]], "Collaborative version control and code review": [[0, null]], "Commits, branches, repositories, forks, clones": [[2, "commits-branches-repositories-forks-clones"]], "Concepts around collaboration": [[2, null]], "Concepts in refactoring and modular code design": [[14, null]], "Conclict resolution, rebasing, and organizational strategies": [[3, null]], "Conda environment": [[10, null]], "Confused about reStructuredText vs. Markdown vs. MyST?": [[7, "confused-about-restructuredtext-vs-markdown-vs-myst"]], "Creating a copy of the repository by \u201cforking\u201d or \u201ccloning\u201d": [[20, "creating-a-copy-of-the-repository-by-forking-or-cloning"]], "Creating branches and commits": [[19, null]], "Creating the virtual environment": [[10, "creating-the-virtual-environment"], [11, "creating-the-virtual-environment"]], "Credit": [[6, null]], "Cross-referencing issues and pull requests": [[5, "cross-referencing-issues-and-pull-requests"]], "Day 1": [[9, "day-1"]], "Day 2": [[9, "day-2"]], "Day 3": [[9, "day-3"]], "Demo: Building documentation with GitHub Actions": [[7, "demo-building-documentation-with-github-actions"]], "Demo: From a script towards a workflow": [[15, null]], "Demonstration": [[21, "demonstration"]], "Discussion": [[17, "discussion-0"], [21, "discussion-0"]], "Di\u00e1taxis": [[7, "diataxis"]], "End-to-end tests": [[17, "end-to-end-tests"]], "Example project: Simulating the motion of planets": [[8, null]], "Example run": [[8, "example-run"]], "Exercise": [[1, "exercise"], [4, "exercise"], [5, "exercise"], [23, "exercise"]], "Exercise preparation": [[1, "prerequisites-0"], [4, "prerequisites-0"], [5, "prerequisites-0"], [20, "prerequisites-0"]], "Exercise: Browsing an existing project (20 min)": [[20, "exercise-0"]], "Exercise: Collaborating within the same repository (25 min)": [[4, "exercise-0"], [5, "exercise-0"]], "Exercise: Copy and browse an existing project": [[20, "exercise-copy-and-browse-an-existing-project"]], "Exercise: Creating branches and commits": [[19, "exercise-creating-branches-and-commits"]], "Exercise: Practice creating commits and branches (20 min)": [[19, "exercise-0"]], "Exercise: Practicing code review (25 min)": [[1, "exercise-0"]], "Exercise: Set up a Sphinx documentation": [[7, "exercise-set-up-a-sphinx-documentation"], [7, "exercise-0"]], "Exercise: Turn your project to a Git repo and share it (20 min)": [[23, "exercise-0"]], "Exercises": [[17, "exercises"]], "Features: roll-back, branching, merging, collaboration": [[21, "features-roll-back-branching-merging-collaboration"]], "Forking a repository": [[2, "forking-a-repository"]], "Forking, cloning, and browsing": [[20, null]], "GitHub, VS Code, or command line": [[20, "github-vs-code-or-command-line"]], "Help and discussion": [[1, "help-and-discussion"], [4, "help-and-discussion"]], "Help! I don\u2019t have permissions to push my local changes": [[4, "help-i-don-t-have-permissions-to-push-my-local-changes"]], "How about staging and committing?": [[22, "how-about-staging-and-committing"]], "How large should a commit be?": [[22, "how-large-should-a-commit-be"]], "How testing is often taught": [[17, "how-testing-is-often-taught"]], "How to approach other people\u2019s repositories with ideas, changes, and requests": [[4, "how-to-approach-other-peoples-repositories-with-ideas-changes-and-requests"]], "How to ask for changes in a pull request": [[1, "how-to-ask-for-changes-in-a-pull-request"]], "How to contribute changes to repositories that belong to others": [[4, null]], "How to create a fork": [[20, null]], "How to make the project more reusable": [[16, null]], "How to modify a pull request to address the review comments": [[1, "how-to-modify-a-pull-request-to-address-the-review-comments"]], "How to release and publish your code": [[13, null]], "How to suggest a change in a pull request as a reviewer": [[1, "how-to-suggest-a-change-in-a-pull-request-as-a-reviewer"]], "How to update your fork with changes from upstream": [[4, "how-to-update-your-fork-with-changes-from-upstream"]], "If we have time left": [[9, "if-we-have-time-left"]], "In-code documentation": [[7, "in-code-documentation"]], "Instructor note": [[7, "instructor-note-0"], [8, "instructor-note-0"], [17, "instructor-note-0"], [20, "instructor-note-0"]], "Introduction to version control with Git and GitHub": [[18, null]], "Is putting software on GitHub/GitLab/\u2026 publishing?": [[23, "is-putting-software-on-github-gitlab-publishing"]], "Keypoints": [[7, "keypoints-0"]], "Learning goals": [[8, null]], "Longer answer": [[17, "solution-0"]], "Many languages allow \u201cdocstrings\u201d": [[7, "many-languages-allow-docstrings"]], "Motivation": [[4, null], [4, null], [17, "motivation"], [21, null]], "Objectives": [[2, "objectives-0"], [7, "objectives-0"], [17, "objectives-0"], [19, "objectives-0"], [20, "objectives-0"], [21, "objectives-0"], [23, "objectives-0"]], "Often a README is enough - checklist": [[7, "often-a-readme-is-enough-checklist"]], "Opening a pull request towards the upstream repository": [[4, "opening-a-pull-request-towards-the-upstream-repository"]], "Optional: How to auto-generate API documentation in Python": [[7, "optional-how-to-auto-generate-api-documentation-in-python"]], "Optional: How to turn your project to a Git repo and share it": [[23, null]], "Plan": [[15, "plan"]], "Possible solutions": [[15, "possible-solutions"]], "Practical advice: How much Git is necessary?": [[22, null]], "Practicing code review": [[1, null]], "Preparation": [[7, "prerequisites-0"], [9, "prerequisites-0"]], "Prerequisites": [[9, "prerequisites"]], "Profiling memory and CPU usage": [[12, null]], "Pull requests can be coupled with automated testing": [[4, "pull-requests-can-be-coupled-with-automated-testing"]], "Pytest": [[17, "pytest"]], "Recovery": [[4, "solution-0"]], "Reproducible research software development using Python": [[9, null]], "Rough plan for this demo": [[15, null]], "Schedule": [[9, "schedule"]], "Script after some work, with command-line interface (spoiler alert)": [[15, "solution-1"]], "Searching in a forked repository will not work instantaneously!": [[20, null]], "Snakemake rules which define a workflow (spoiler alert)": [[15, "solution-2"]], "Solution": [[21, "solution-0"]], "Solution and hints": [[5, "solution-and-hints"]], "Solution and walk-through": [[19, "solution-and-walk-through"], [20, "solution-and-walk-through"]], "Starting point (spoiler alert)": [[15, "solution-0"]], "Summary": [[1, "summary"], [4, "summary"], [5, "summary"], [19, "summary"], [20, "summary"]], "Synchronizing changes between repositories": [[2, "synchronizing-changes-between-repositories"]], "Talking about code": [[21, "talking-about-code"]], "Virtual environment": [[11, null]], "We will not focus on \u2026": [[8, null]], "What else is possible": [[17, "what-else-is-possible"]], "What if you need more than a README?": [[7, "what-if-you-need-more-than-a-readme"]], "What is a protected branch? And how to modify it?": [[5, "what-is-a-protected-branch-and-how-to-modify-it"]], "What level of branching complexity is necessary for each project?": [[22, "what-level-of-branching-complexity-is-necessary-for-each-project"]], "What to avoid": [[22, "what-to-avoid"]], "What we typically like to snapshot": [[21, "what-we-typically-like-to-snapshot"]], "Where to read more": [[7, "where-to-read-more"]], "Where to start": [[17, "where-to-start"]], "Why do we need to keep track of versions?": [[21, "why-do-we-need-to-keep-track-of-versions"]], "Why? \ud83d\udc97\u2709\ufe0f to your future self": [[7, "why-to-your-future-self"]], "Working on the command line? Use \u201cgit status\u201d all the time": [[22, "working-on-the-command-line-use-git-status-all-the-time"]], "Writing useful commit messages": [[22, "writing-useful-commit-messages"]]}, "docnames": ["collaboration", "collaboration/code-review", "collaboration/concepts", "collaboration/demo-discussion", "collaboration/forking-workflow", "collaboration/same-repository", "credit", "documentation", "example", "index", "installation/conda", "installation/virtual-environment", "profiling", "publishing", "refactoring-concepts", "refactoring-demo", "reusable", "testing", "version-control", "version-control/branching-and-committing", "version-control/browsing", "version-control/motivation", "version-control/practical-advice", "version-control/sharing"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["collaboration.md", "collaboration/code-review.md", "collaboration/concepts.md", "collaboration/demo-discussion.md", "collaboration/forking-workflow.md", "collaboration/same-repository.md", "credit.md", "documentation.md", "example.md", "index.md", "installation/conda.md", "installation/virtual-environment.md", "profiling.md", "publishing.md", "refactoring-concepts.md", "refactoring-demo.md", "reusable.md", "testing.md", "version-control.md", "version-control/branching-and-committing.md", "version-control/browsing.md", "version-control/motivation.md", "version-control/practical-advice.md", "version-control/sharing.md"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"": [1, 5, 17, 19, 20, 22, 23], "0": [7, 8, 10, 11, 15, 17, 19, 22, 23], "00": 9, "03": 8, "04": 8, "07": 8, "08": 23, "09": [8, 9, 21], "1": [7, 8, 13, 15, 16, 17], "10": [8, 9, 10, 16, 17], "100": [8, 21, 23], "12": [7, 8, 9, 17, 23], "123": 22, "13": [8, 9], "1343ac0": 21, "14": 9, "15": [7, 9, 13, 15, 16, 17, 22], "16": [8, 9], "17": [8, 23], "18": [8, 9], "19": 21, "19k": 23, "2": [7, 10, 11, 13, 16, 17, 22, 23], "20": [8, 9, 16, 17], "2013": 7, "2019": 21, "2020": 21, "2021": 21, "2023": 21, "2024": [7, 21], "21": [8, 21, 23], "24": 8, "244c993": 20, "25": 8, "26": 8, "27": 8, "273": 7, "28": 23, "29": 8, "3": [7, 8, 10, 15, 17, 23], "30": [7, 9, 13, 15], "300": 15, "31": 8, "33": 8, "35": 8, "36": [8, 23], "37": 8, "38": 8, "39": 8, "4": [15, 23], "40": [3, 8, 9], "42": 8, "44": 8, "45": [8, 9], "46": 8, "48": 8, "5": [7, 8, 15, 17], "50": [7, 8, 9, 22], "5000": 4, "51": 8, "53": 8, "54": 8, "55": 8, "56": 8, "59": 8, "6": [8, 23], "60": 8, "62": 8, "63": 8, "64": 8, "66": 8, "68": 8, "69": 8, "7": 23, "70": 8, "74": 8, "75": 8, "77": 8, "78": 8, "8": [7, 8, 15, 17], "81": 8, "82": 8, "83": 8, "84": 8, "85": 8, "86": 8, "88": 8, "9": 4, "90": 9, "92": 8, "93": 8, "95": 8, "96": 8, "97": 8, "98": 8, "A": [1, 2, 5, 9, 10, 11, 17, 19, 20, 22], "And": 1, "As": 1, "At": [7, 8, 20], "BY": 6, "Be": [2, 5, 8, 23], "Being": [1, 4, 5], "But": [1, 4, 5, 17, 20, 21, 22, 23], "By": 20, "For": [5, 7, 19, 23], "If": [1, 4, 5, 7, 10, 17, 20, 22, 23], "In": [1, 2, 4, 5, 9, 10, 11, 15, 19, 20, 21, 23], "It": [1, 2, 4, 5, 7, 8, 17, 20, 21, 22, 23], "No": 17, "Not": [7, 19, 22], "On": [19, 20, 22], "One": 9, "Or": [1, 4, 21, 22], "The": [1, 2, 4, 5, 6, 7, 8, 15, 17, 19, 20, 22, 23], "Then": [4, 5, 7, 8, 19, 23], "There": 7, "These": [4, 20, 21], "To": [4, 5, 20, 23], "With": [19, 21], "__main__": 15, "__name__": 15, "__pycache__": 22, "_build": 7, "abl": [1, 2, 4, 5, 7, 8, 15, 23], "about": [1, 4, 5, 19, 23], "abov": [1, 10, 11, 17, 19, 20, 23], "acceler": 8, "accept": [1, 4, 5, 8, 22], "access": [2, 4, 5, 20, 23], "accident": [1, 5, 23], "account": [2, 4, 5, 9, 20, 23], "achiev": [9, 19], "across": 2, "action": [4, 13], "activ": [5, 10, 11], "actual": [9, 19, 20], "ad": [1, 4, 7, 19], "add": [1, 5, 7, 12, 13, 17, 19, 20, 22, 23], "addit": [7, 19, 23], "address": [5, 23], "adipisc": 7, "adjust": [4, 5, 7, 15, 19, 22], "advanc": 20, "advantag": 10, "advic": [9, 18, 19], "affect": 21, "after": [1, 4, 20, 23], "again": [1, 4, 5, 8, 17, 22], "agreement": 4, "algorithm": 8, "alia": 20, "aliqua": 7, "aliquip": 7, "all": [2, 5, 6, 7, 8, 9, 10, 15, 17, 19, 20, 23], "allclos": 17, "allow": 5, "almost": 7, "along": 20, "alpha": [15, 22], "alreadi": [4, 10, 19, 20, 23], "also": [1, 2, 4, 5, 6, 7, 9, 11, 17, 19, 20, 22, 23], "altern": [2, 5, 20], "alwai": [1, 4, 21, 22], "am": 21, "ambiti": 22, "amend": 1, "amet": 7, "an": [1, 4, 7, 8, 9, 10, 11, 17, 19, 21, 23], "anaconda": 10, "ani": [4, 7, 8, 17, 19, 22, 23], "anim": [7, 8], "annot": [20, 21], "anoth": [7, 19, 20], "answer": [19, 20, 21], "anticip": 21, "anyon": 20, "appear": 19, "appli": [8, 9, 15], "applic": 20, "approach": [7, 15], "apptain": 9, "ar": [1, 2, 4, 5, 7, 8, 9, 10, 19, 21, 22, 23], "arbitrari": 17, "area": 22, "aren": [19, 20], "argument": [8, 17], "around": [0, 9, 19], "arrai": 17, "arrow": 23, "articl": 5, "ask": [4, 5, 10, 15], "assert": [7, 17], "assum": 9, "attach": [2, 19], "attent": 4, "august": [7, 21], "aut": 7, "authent": [1, 4, 5], "author": [7, 20, 21, 22, 23], "auto": 15, "autoapi": [7, 10, 11], "autoapi_dir": 7, "autoapi_ignor": 7, "autobuild": [7, 10, 11], "autom": 9, "automat": [1, 2, 4, 5, 7, 17, 19], "avail": [9, 10, 15], "avoid": 20, "awar": 20, "b": [7, 17, 23], "back": [2, 19, 20], "background": 15, "backup": 23, "bad": [21, 22], "base": [2, 15, 17, 22], "bash": [7, 10, 17, 20], "basi": 5, "basic": 19, "becam": 7, "becaus": [15, 22, 23], "becom": 20, "been": 6, "befor": [4, 5, 14, 19, 20, 21, 22], "begin": 7, "beginn": 10, "behind": 4, "being": 4, "belong": [0, 9], "below": [1, 4, 5, 7, 15, 19, 20, 23], "benefit": 15, "best": 16, "better": [1, 7, 17, 20, 21, 22], "between": [5, 8, 19, 21], "big": [8, 15, 20], "bin": [10, 11], "binari": 15, "binary_imag": 15, "bioconda": 10, "black": [10, 11, 15], "blame": 20, "blob": [17, 21], "block": [7, 9], "blogdown": 7, "bodi": 8, "book": [4, 5], "bookdown": 7, "both": [4, 5, 19, 20], "bottom": [5, 19, 20], "branch": [1, 4, 7, 9, 17, 18, 20, 23], "brand": 23, "break": [9, 17, 21, 22], "brief": 19, "bright": 15, "broke": [17, 21], "brows": [9, 17, 18, 21, 22], "browser": [7, 19, 20], "bug": 21, "build": [9, 15, 17, 19, 20], "built": 7, "bunch": [20, 23], "button": [1, 19, 20, 23], "c": 20, "c7f0e8bfc718be04525847fc7ac237f470add76": 2, "cach": [7, 17], "calcul": 8, "call": [2, 5, 19, 20, 23], "can": [1, 2, 5, 7, 8, 12, 15, 17, 19, 21, 22, 23], "cannot": 5, "caption": 7, "carri": 21, "case": [1, 2, 4, 5, 7, 8, 17, 20, 23], "catch": 17, "cc": 6, "cd": 20, "celsiu": 7, "central": 2, "centroid": 15, "certain": [19, 21], "cff": 13, "chang": [0, 6, 7, 9, 17, 19, 20, 21, 22, 23], "changelog": 22, "channel": [7, 10, 15, 17], "check": [4, 5, 7, 17, 23], "checklist": 5, "checkout": [7, 17], "checksum": 21, "choic": 22, "choos": [8, 9, 23], "chose": 23, "chosen": 8, "cillum": 7, "citabl": 23, "citat": [7, 17], "class": 20, "cleanup": [7, 17], "clear": 4, "cli": 15, "click": [1, 4, 5, 7, 10, 11, 15, 17, 19, 20, 23], "clone": [4, 5, 7, 9, 18, 21], "close": [1, 4, 5], "cloud": 5, "cmap": 15, "code": [2, 4, 5, 8, 9, 11, 15, 17, 19, 22, 23], "codecov": 17, "coderefineri": [6, 7, 10, 11], "collabor": [1, 6, 9, 23], "colleagu": 2, "collect": [5, 15, 20, 22], "colon_f": 7, "color": 15, "com": [4, 5, 7, 17, 19, 20, 21, 22, 23], "combin": [1, 22], "come": [5, 20], "comfort": 20, "comma": [8, 15], "command": [2, 5, 8, 10, 19, 23], "commemor": 2, "comment": [5, 7], "commentari": 7, "commit": [1, 4, 5, 9, 17, 18, 21, 23], "commodo": 7, "common": [1, 2, 19], "commun": [2, 5, 9], "compar": [7, 17, 21, 22], "complet": [2, 22, 23], "complic": 17, "compon": 17, "compress": 23, "comput": [2, 4, 8, 20, 21, 23], "concept": [0, 9], "conceptu": 21, "conclict": [0, 9], "conda": [7, 9, 17], "conf": 7, "config": 20, "configur": [5, 21, 23], "conflict": 9, "connect": [5, 9, 15], "consectetur": 7, "consequat": 7, "consid": [5, 22, 23], "consist": 23, "constant": [17, 20], "construct": [1, 5, 7, 9], "contain": [2, 4, 8, 9, 16, 22, 23], "content": 7, "context": [1, 20, 22], "continu": [1, 10, 20], "contribut": [0, 2, 5, 7, 9], "control": [7, 9, 19, 20, 21, 23], "convent": [15, 22], "converg": 22, "convert": 7, "convert_to_grai": 15, "copi": [2, 5, 7, 10, 11, 19, 21, 23], "copyright": [5, 7], "core": [8, 15], "corner": 5, "correct": [4, 23], "correspond": [17, 20], "cost": 19, "could": [1, 2, 5, 8, 19, 20, 23], "count": [15, 23], "counter": 1, "coupl": 7, "cours": [9, 10, 11, 20], "cov": [10, 11, 17], "cover": 7, "coverag": 17, "coveral": 17, "cprofil": 12, "cpu": 9, "creat": [1, 2, 4, 6, 7, 9, 13, 15, 17, 18, 21, 22, 23], "cross": [4, 15, 22], "csv": 8, "culpa": 7, "cupidatat": 7, "current": [19, 23], "dai": [5, 22], "danger": [22, 23], "data": [2, 8, 17, 21], "date": [4, 21], "db": 7, "debrief": 9, "decad": 22, "decid": [2, 10, 11], "decor": [19, 20], "def": [7, 15, 17], "default": [4, 15, 19, 20, 21, 23], "defin": [4, 19, 20], "degre": 7, "delet": 5, "deliber": 1, "delta": 23, "demo": [3, 9], "demonstr": [4, 8, 16, 20], "depend": [5, 7, 9, 10, 11, 15, 16, 17, 20], "deploi": [7, 13, 17], "describ": [4, 5, 17, 20], "descript": [5, 23], "deserunt": 7, "design": [9, 17, 19, 20, 22], "detail": [1, 4, 7, 8, 22], "detect": [15, 22], "develop": [2, 14, 19, 22], "diataxi": 7, "did": [19, 21], "didn": 19, "diff": 19, "differ": [2, 4, 19, 20, 21, 23], "difficult": [4, 17], "direct": [4, 5, 7], "directli": [1, 5, 19], "directori": [7, 10, 11, 17, 20, 21, 22, 23], "disadvantag": 10, "discard": 21, "discov": 21, "discuss": [2, 3, 7, 8, 9, 15, 16, 20, 22], "disk": 19, "distanc": 17, "distribut": 9, "diverg": 19, "divid": 2, "do": [1, 2, 4, 5, 7, 19, 20, 22, 23], "doc": [7, 21], "document": [6, 9, 10, 11, 14, 15, 21], "doe": [4, 7, 17, 19], "doi": 23, "dolor": 7, "don": [1, 5, 7, 9, 19, 20, 23], "done": [4, 5, 19, 20, 23], "dot": [19, 20], "doubl": 4, "down": [5, 23], "download": [2, 10], "dpi": 15, "driven": 14, "ds_store": 7, "dui": 7, "dure": [1, 4, 22], "e": [2, 7, 10, 20], "ea": 7, "each": [1, 2, 4, 5, 8, 19], "earli": [5, 22], "easi": [1, 8, 10, 19, 20, 23], "easier": [7, 17, 22], "easili": [19, 22], "edit": 19, "editor": [9, 20], "effici": 8, "effort": 5, "either": [1, 11, 19, 20, 23], "eiusmod": 7, "el": [7, 17], "elit": 7, "els": [1, 5, 22], "email": 5, "empti": [22, 23], "emul": 7, "en": 5, "enabl": [1, 22], "encourag": [5, 15, 19, 20], "end": [5, 15, 22], "enemi": 22, "engin": [6, 9, 22], "enim": 7, "enough": [19, 20, 22], "enter": [19, 20], "entir": 19, "enumer": 23, "env": 10, "environ": [7, 9, 17], "episod": [1, 4, 5, 7, 15, 19, 20], "equat": 7, "error": [4, 7, 23], "especi": 22, "ess": 7, "est": 7, "et": 7, "etc": [9, 22], "eu": 7, "even": [1, 5, 23], "event_nam": 7, "everi": [7, 17, 20], "everyon": 2, "everyth": 7, "evolv": 7, "ex": 7, "exactli": 4, "exampl": [4, 5, 7, 9, 12, 15, 17, 19, 20, 21, 22, 23], "excepteur": 7, "exclude_pattern": 7, "execut": 23, "exercis": 16, "exercit": 7, "exist": [4, 15, 19, 21, 23], "expand": 15, "expect": [4, 8, 17, 19], "experi": [7, 17, 21], "expert": [5, 23], "explain": [7, 15, 23], "explan": [17, 23], "explor": [7, 15, 20], "extend": [7, 17], "extens": [7, 19, 20], "extern": 7, "extra": [1, 7], "extract": 15, "f": 15, "fact": 1, "fail": [4, 17], "fals": [7, 15, 17], "familiar": [1, 4, 5], "fast": 10, "faster": 22, "favorit": 9, "favourit": 22, "featur": [4, 22], "februari": 21, "feedback": [4, 5, 9], "feel": [7, 17], "fetch": [2, 4, 5], "few": [1, 2, 4, 20, 22, 23], "fiction": 15, "field": 1, "figsiz": 15, "figur": 15, "file": [1, 2, 5, 7, 8, 10, 11, 13, 15, 17, 21, 22, 23], "file_nam": 15, "filenam": [19, 20], "filter": 15, "final": [7, 8, 19, 23], "find": [7, 15, 19, 20, 23], "findabl": 23, "fine": 23, "finish": [5, 23], "first": [2, 4, 5, 7, 8, 17, 19, 20, 22, 23], "fix": [1, 4, 5, 22], "flask": 22, "flavor": 7, "flit": [10, 11, 13], "float": 7, "folder": [20, 23], "follow": [6, 7, 9, 10, 15, 19, 20, 21, 23], "forc": [5, 8, 17, 23], "force_between_planet": [17, 21], "force_magnitud": 17, "force_orphan": 7, "forg": [7, 10, 17], "forget": [5, 7], "fork": [7, 9, 18, 21], "form": [5, 19, 22], "format": 15, "formatt": 11, "former": 2, "found": [15, 21], "fourth": 15, "fr": 7, "fragment": 1, "free": [7, 22], "frequent": 19, "from": [1, 2, 5, 7, 9, 17, 19, 20, 21, 22, 23], "fugiat": 7, "fulli": [22, 23], "fun": 4, "function": [7, 14, 15, 17, 19, 20], "futur": [5, 8, 22], "g": [2, 7, 10, 17, 20], "gate": 1, "gaussian": 15, "gener": [2, 8, 15, 17, 19, 22], "generate_plot": 15, "get": [1, 2, 4, 5, 7, 9, 15, 17, 19, 20, 22, 23], "ggplot2": 22, "gh": 7, "git": [2, 4, 5, 6, 7, 9, 10, 11, 15, 19, 20, 21], "github": [1, 2, 4, 6, 9, 13, 19, 21], "github_token": 7, "gitignor": [19, 22, 23], "gitlab": [2, 4, 5, 7, 21], "give": [1, 5, 9, 15, 21], "given": 17, "glob_wildcard": 15, "global": 20, "go": [5, 7, 9, 19, 20, 21, 23], "goal": [1, 15, 17], "goe": [19, 20], "gone": 21, "good": [1, 5, 7, 9, 10, 15, 17, 22, 23], "googl": 21, "gopher": [19, 21], "grab": 4, "grai": 15, "grant": 4, "graph": [19, 21], "graphic": 10, "gravit": [8, 17, 20], "gray_imag": 15, "great": [21, 22], "green": 23, "grep": 20, "group": [1, 5, 9], "guarante": 17, "guid": [7, 17], "ha": [2, 5, 19, 20, 22], "half": 5, "hand": [9, 15], "happen": [5, 19, 23], "hard": 22, "harder": 1, "hash": 19, "have": [1, 2, 5, 6, 7, 17, 19, 20, 21, 23], "head": 7, "hear": 5, "help": [5, 13, 15, 22, 23], "here": [1, 4, 5, 8, 10, 12, 15, 19, 23], "high": 2, "highlight": [7, 17], "hint": [1, 19, 20], "histor": 20, "histori": [1, 2, 23], "hold": 17, "hopefulli": [7, 8, 21], "host": 21, "hover": 20, "how": [0, 2, 8, 9, 14, 15, 18, 19, 21], "howev": 5, "html": 7, "html_theme": 7, "http": [4, 5, 6, 7, 9, 17, 19, 20, 21, 22, 23], "huge": 22, "hugo": 7, "human": 5, "hypothesi": 17, "i": [1, 2, 6, 8, 9, 10, 11, 12, 15, 18, 19, 20, 21], "icon": [5, 7, 19, 23], "id": 7, "idea": [21, 22], "ideal": 5, "identifi": [2, 19, 20, 23], "ignor": [7, 15], "illustr": [19, 20], "imag": [4, 7, 10, 11, 15, 19, 21], "image_fil": 15, "image_smooth": 15, "imagin": 15, "immut": 15, "imperfect": 22, "implement": [1, 2], "import": [8, 15, 19, 20, 22], "imposs": 7, "improv": [1, 7, 9, 15, 22], "imread": 15, "imshow": 15, "inbox": 5, "incididunt": 7, "includ": [5, 7], "incomplet": 1, "increas": 22, "incredibli": 5, "independ": [2, 7], "index": [7, 20], "indic": 4, "individu": 9, "info": 1, "inform": [4, 5, 9, 19, 20, 22], "ingredi": 4, "init": [7, 17, 23], "initi": [8, 23], "input": [8, 15], "insensit": [5, 20], "insid": 20, "insight": [19, 20, 21], "inspect": 20, "inspir": [19, 21, 22], "instal": [5, 7, 10, 11], "instanc": [2, 17, 20], "instead": [1, 5, 7, 17, 20, 23], "instruct": [4, 5, 7, 9, 19], "instructor": [4, 5], "integr": 7, "intel": 12, "intent": 2, "interact": 5, "interchang": [5, 19], "interest": [1, 5], "interf": 21, "interfac": [1, 5, 10, 19, 23], "interfer": 19, "intern": [7, 8], "intro": 6, "introduc": [1, 15], "introduct": [9, 16], "invent": 7, "invit": 5, "io": [6, 15], "ipsum": 7, "ipywidget": [10, 11], "irur": 7, "is_flag": 15, "isn": 1, "isol": [10, 11], "isort": [10, 11], "issu": [4, 20, 22], "its": 17, "itself": 7, "jekyl": 7, "job": [7, 17], "john": 7, "join": 17, "joss": 7, "julia": 22, "jupyt": 15, "jupyterlab": [10, 11], "jupyterlab_code_formatt": 10, "just": [1, 4, 5, 7, 20, 22, 23], "keep": [1, 4, 7], "kei": 7, "kelvin": 7, "kelvin_to_celsiu": 7, "kept": 7, "keyword": [5, 20], "kib": 23, "kill": 17, "kind": [1, 4, 5, 21], "knitr": 7, "know": [1, 4, 5, 10, 17, 20, 21, 22], "l": 23, "l31c5": 21, "l39": 21, "label": 15, "labeled_imag": 15, "labor": 7, "labori": 7, "laborum": 7, "languag": [9, 17], "laptop": [2, 21], "larg": [10, 23], "larger": [1, 15], "later": [1, 2, 4, 5, 7, 8, 15, 19, 20, 21, 22], "latest": [2, 7, 17, 21], "latex": 21, "layout": 22, "lead": 9, "learn": [1, 2, 4, 5, 7, 9, 17, 22], "learner": 5, "least": [1, 7], "leav": 23, "left": [5, 19, 20], "len": 15, "less": [10, 11, 17], "lesson": [1, 4, 7], "let": [4, 7, 17, 19, 22, 23], "level": 2, "leverag": 7, "licens": [5, 7, 9, 10, 13, 20, 23], "like": [1, 2, 4, 5, 7, 8, 10, 15, 17, 19, 20, 22, 23], "limit": [7, 17], "line": [1, 2, 4, 5, 8, 17, 19, 23], "line_profil": 12, "linear": 20, "link": [4, 5, 7, 23], "linkcheck": 7, "lint": 15, "linux": 9, "list": [10, 20, 21], "ll": [19, 20], "local": [1, 5, 7, 17, 20, 23], "locat": [10, 11, 19], "locate_posit": 15, "log": [15, 19, 20, 23], "login": 23, "long": 20, "longer": 22, "look": [1, 4, 7, 8, 15, 17, 19, 20], "lorem": 7, "lose": 22, "lot": [7, 21, 23], "low": 7, "m": [11, 19, 23], "made": [2, 5, 6], "magna": 7, "magnifi": 20, "mai": [7, 19, 22], "main": [1, 2, 4, 7, 15, 17, 20, 22, 23], "mainli": 5, "maintain": 5, "make": [1, 2, 4, 7, 8, 9, 10, 15, 17, 19, 20, 21, 22, 23], "mamba": [7, 17], "manag": [5, 11, 16], "mani": [5, 15, 22], "manuscript": [19, 21], "mar": 23, "mark": [5, 19, 22], "markeredgewidth": 15, "markers": 15, "markup": 7, "mass": 8, "mass1": 17, "mass2": 17, "master": [2, 19, 23], "match": [17, 20], "materi": [4, 6, 7, 17, 20, 21], "math": 7, "matplotlib": [7, 10, 11, 15, 17], "maxdepth": 7, "mayb": 4, "md": [7, 19], "me": [19, 21, 22], "mean": [1, 2, 5, 19, 23], "meaning": 19, "meant": [5, 20], "measur": [7, 15], "mechan": [1, 2], "memori": 9, "mention": 5, "merg": [1, 2, 4, 5, 9, 19, 20, 22], "messag": [5, 19, 20, 21], "metadata": [19, 21], "method": [19, 23], "mib": 23, "micromamba": [7, 10, 17], "might": [4, 20, 22, 23], "mileston": 22, "min": [3, 7, 9, 13, 15, 16, 17], "mind": 23, "mini": 6, "miniconda": 10, "miniconda3": 10, "miniforg": 10, "minim": [7, 10], "minor": 4, "minu": 1, "minut": 20, "mistak": 1, "mkdoc": 7, "modern": [4, 11], "modif": [4, 7, 19], "modifi": 8, "modul": [15, 17], "modular": [9, 15], "mollit": 7, "more": [1, 9, 10, 11, 15, 17, 19, 20, 21, 22], "most": [5, 8, 9, 19, 20, 22], "motion": 9, "motiv": [9, 16, 18, 22], "mous": 4, "move": [15, 19], "mp4": 8, "much": [1, 4, 5, 7, 9, 17, 18, 19, 20, 21], "multipl": [2, 8, 19, 20, 22], "must": 5, "mutant": 17, "mutat": 17, "mutmut": 17, "my": [5, 21, 22, 23], "myproject": [21, 23], "myscript": 23, "myst": [10, 11], "myst_enable_extens": 7, "myst_pars": 7, "myvers": 21, "n": [8, 15], "name": [5, 7, 10, 15, 17, 19, 20, 23], "navig": [4, 20], "nbdime": [10, 11], "necessari": [1, 2, 5, 7, 9, 18, 19], "need": [1, 2, 5, 9, 10, 11, 17, 19, 20, 22, 23], "neg": 7, "network": [19, 21], "never": [22, 23], "new": [1, 2, 4, 7, 10, 11, 17, 20, 22, 23], "newaddress": 5, "newfeatur": 21, "next": [1, 17, 19, 23], "nice": [5, 19, 20], "nisi": 7, "nois": 15, "non": 7, "none": 23, "normal": 19, "nostrud": 7, "note": [4, 5, 19, 22, 23], "notebook": [15, 17], "noth": 17, "notic": 4, "notif": 5, "notifi": 5, "now": [1, 4, 5, 7, 11, 15, 17, 19, 20, 21, 22, 23], "np": 17, "npz": 8, "nulla": 7, "num": 8, "number": [1, 4, 5, 8, 15], "numer": 8, "numpi": [7, 10, 11, 17, 22], "oauth": 23, "object": [14, 15], "observ": [4, 5, 7], "occaecat": 7, "occurr": 20, "offer": [19, 20, 23], "officia": 7, "often": [2, 5, 19, 22], "oh": 21, "old": 17, "older": 23, "onc": [1, 4, 5, 9, 19, 22, 23], "one": [1, 2, 4, 5, 7, 9, 10, 15, 17, 19, 20, 22, 23], "onelin": [19, 20], "onli": [1, 5, 7, 9, 10, 19, 20, 22, 23], "onlin": [15, 23], "oop": 22, "open": [1, 7, 10, 15, 19, 20, 23], "oper": [2, 8], "opinion": 7, "option": [9, 15, 18, 20], "order": [1, 2, 7], "org": [7, 17], "organ": [5, 15, 20], "organiz": [0, 9], "orient": 14, "origin": [4, 5, 6, 7, 15, 20, 23], "other": [0, 1, 2, 5, 7, 8, 9, 15, 19, 20, 21, 22, 23], "otherwis": [1, 22], "our": [1, 5, 7, 8, 15, 17, 19, 20, 21, 23], "out": [1, 4, 7, 19, 20, 21, 22], "output": [7, 8, 10, 15, 20], "output_fil": 15, "over": [13, 19, 20, 22], "overlai": 15, "overview": [7, 13, 19], "overwrit": 23, "own": [2, 5, 7, 8, 9, 17, 19, 20, 21, 23], "p": 22, "pack": 23, "packag": [7, 9, 13], "page": [5, 7, 19, 20, 23], "pai": 4, "pair": [8, 20], "panda": [10, 11, 22], "paper": [2, 21], "paragraph": 22, "parallel": 21, "paramet": 7, "parent": 2, "pariatur": 7, "parser": [7, 10, 11], "part": [1, 5, 7, 13, 15, 16, 17, 19, 23], "particip": [5, 9, 15], "particular": 2, "pass": [4, 17], "past": [7, 10, 11, 20, 23], "path": [15, 20, 23], "pattern": [14, 19], "peaceiri": 7, "pencil": 19, "peopl": [5, 17, 19, 20, 21, 22], "perfect": [17, 22], "perhap": 2, "permalink": 21, "permiss": 7, "persist": 23, "person": [2, 20, 22], "phd": 2, "pictur": [8, 20], "pid": 23, "piec": [5, 15, 20], "pip": 11, "pixi": 10, "place": [5, 7], "placehold": 23, "plain": 21, "planet": [7, 9, 17, 19, 20, 21], "plaqu": 2, "platform": 21, "pleas": [1, 4, 5, 10, 19, 21, 23], "plot": 15, "plot_posit": 15, "plt": 15, "plu": [1, 23], "png": 15, "point": [4, 5, 7, 17, 19, 21, 23], "pointer": 2, "popular": [5, 7], "portabl": 8, "posit": [8, 15], "position1": 17, "position2": 17, "possibl": [1, 4, 5, 8, 9, 20, 21, 22], "possibli": [4, 23], "post": [7, 17], "postpon": 22, "power": [1, 23], "practic": [0, 5, 9, 15, 16, 18, 21], "pre": 19, "precis": 5, "prefer": [9, 20], "prevent": 23, "preview": 20, "previou": [1, 4, 5, 19, 21], "previous": [5, 7, 19], "print": [2, 7, 15, 19], "probabl": [4, 7, 17, 23], "problem": [1, 4, 5, 15, 21], "process": [1, 15, 19], "process_data": 15, "product": 1, "profil": 9, "program": 19, "progress": [5, 7], "proident": 7, "project": [2, 4, 5, 7, 9, 10, 11, 15, 17, 18, 19, 21], "properti": [15, 17], "propos": [4, 5], "protect": 22, "provid": [7, 20, 22], "public": [2, 23], "publicli": 23, "publish": [5, 9], "publish_branch": 7, "publish_dir": 7, "pull": [2, 17, 21, 22, 23], "pull_request": [7, 17], "pure": [14, 15], "purpos": [4, 7, 17, 19], "push": [1, 2, 7, 17, 23], "put": 5, "px": 8, "py": [7, 8, 12, 15, 17, 20, 21, 23], "pyc": 22, "pyflam": 12, "pyinstrument": 12, "pypi": [9, 13], "pyplot": 15, "pytest": [10, 11], "python": [8, 10, 13, 15, 16, 17], "python3": 11, "pz": 8, "q": 9, "qualiti": [1, 9, 15], "quarto": 21, "question": [15, 20, 21], "qui": 7, "quick": 19, "quickstart": 7, "quit": [1, 23], "r": [7, 11, 17], "ran": 23, "random": 8, "rang": 1, "rather": [7, 22], "read": [2, 4, 5, 8, 15], "readi": 5, "readm": 23, "readthedoc": 7, "real": [17, 20, 23], "realli": [22, 23], "reason": [4, 20, 22], "rebas": [0, 9], "rebuild": 7, "receiv": [5, 9], "recip": 4, "recogn": [1, 5, 21], "recommend": [5, 7, 9, 10, 19, 20], "record": [9, 19], "recov": 22, "recoveri": 23, "red": 23, "redirect": [20, 23], "reduc": 15, "ref": [4, 7], "refactor": [9, 15], "refer": [2, 4, 5, 7, 17, 22, 23], "refresh": 7, "regard": 7, "region": 15, "regionprop": 15, "regist": 5, "regress": 17, "reject": 4, "rel": 15, "releas": [2, 7, 9, 17, 19, 22], "relev": [2, 22], "reload": 23, "rememb": [1, 20, 21, 22, 23], "remot": [2, 4, 5, 23], "remov": 23, "renam": 23, "render": 7, "repair": 22, "repeat": [5, 19], "replac": [4, 19, 23], "repo": [9, 18], "report": 17, "repositori": [0, 1, 7, 9, 15, 19, 21, 22, 23], "reprehenderit": 7, "repres": [15, 23], "reproduc": [7, 21], "request": [2, 17, 21, 22], "requir": [1, 4, 5, 7, 9, 11, 19, 20], "research": [1, 5, 6], "resid": 2, "resolut": [0, 9], "resolv": 5, "respons": 5, "restart": 23, "restrict": 10, "result": [1, 4, 8, 15, 17], "return": [7, 15, 17], "reus": [6, 23], "reusabl": [8, 9], "review": [4, 7, 9, 17, 21, 22], "reviv": 17, "rewrit": 17, "rgb2grai": 15, "rgb_imag": 15, "right": [4, 5, 7, 17, 19, 20, 23], "risk": 22, "rmd": 7, "robust": 9, "rst": 7, "rstudio": [7, 23], "rtd": 7, "ruff": 15, "run": [4, 7, 10, 15, 17, 20, 23], "rw": 23, "rx": 15, "safe": 22, "safeguard": 22, "safer": 11, "sai": [5, 19, 20], "same": [0, 1, 2, 7, 9, 19, 20, 21, 22, 23], "sandbox": 13, "save": [7, 22], "savefig": 15, "saw": [19, 20], "scale": 8, "scalen": [10, 11, 12], "scari": 17, "scikit": [10, 11], "scipi": 22, "screen": [15, 20], "screenshot": 23, "script": [8, 9, 17, 21], "scroll": 5, "seaborn": [10, 11], "search": [7, 21], "second": 22, "secret": 7, "section": [4, 7], "sed": 7, "see": [1, 4, 5, 7, 10, 17, 19, 20, 23], "seem": 19, "seen": 9, "select": [1, 2, 7, 20, 23], "selector": 19, "selv": 8, "send": [21, 23], "sens": 19, "sensit": 19, "separ": [8, 19], "septemb": 21, "server": [2, 7], "servic": [4, 17, 23], "session": 20, "set": [4, 5, 9, 23], "setup": [7, 17, 20, 23], "sever": 19, "sh": 17, "share": [5, 9, 15, 18, 19, 21], "shell": [7, 10, 15, 17], "short": [4, 17, 19, 23], "shortcut": 20, "shortli": 20, "should": [4, 5, 7, 19, 20, 23], "show": [15, 19, 20], "side": [5, 19], "sidebar": [19, 20], "sigma": 15, "sign": [1, 19], "signal": 5, "signific": 19, "silli": 4, "similar": [19, 20, 21, 23], "simpl": [8, 15, 17, 20, 22, 23], "simpler": 4, "simplifi": 22, "simul": [9, 17, 20, 21], "sinc": [5, 7, 20, 21, 23], "singl": [1, 19], "sint": 7, "sit": 7, "site": 7, "situat": 2, "size": [10, 22], "skimag": 15, "skip": [5, 9], "slightli": 20, "small": [1, 10, 15, 22, 23], "smaller": [2, 22], "snakefil": 15, "snakemak": [9, 10], "snapshot": [2, 17, 19], "so": [1, 4, 5, 19, 20, 22, 23], "softwar": [4, 6, 10, 11, 13, 15, 21], "solut": [1, 4], "solv": [1, 15], "some": [1, 4, 5, 7, 9, 12, 19, 20, 21, 22, 23], "somebodi": [1, 4, 5, 17, 21], "someon": [5, 22], "someth": [1, 4, 5, 7, 17, 21, 22], "sometim": 22, "somewhat": 20, "somewher": 21, "soon": 2, "sound": 21, "sourc": [1, 4, 7, 10, 11, 19, 20, 21, 23], "space": 19, "specif": [1, 17, 19], "specifi": [5, 8], "sphinx": [10, 11], "sphinx_rtd_them": [7, 10, 11], "split": 22, "spy": 12, "ssh": [9, 20, 23], "stabl": 8, "stai": 2, "standard": 20, "star": 15, "star_posit": 15, "start": [4, 5, 7, 8, 19, 20, 21, 22, 23], "stash": 22, "state": 19, "statu": 19, "step": [1, 4, 5, 7, 8, 9, 15, 17, 19, 20, 22, 23], "stick": 19, "sticki": 19, "still": [17, 19, 23], "store": 20, "strategi": [0, 9], "strongli": 1, "structur": [15, 17], "studi": [8, 9], "style": 22, "submit": [2, 4, 5], "submitt": 1, "success": 22, "successfulli": 5, "suggest": [2, 4, 5, 15, 21], "suitabl": [5, 21], "summar": [6, 22], "sunt": 7, "supercomput": 2, "support": 19, "sure": [4, 5, 9, 10, 19, 21, 22, 23], "switch": 23, "symbol": [1, 20, 23], "sync": 4, "syrupi": 17, "system": [8, 21, 23], "systemat": 7, "t": [1, 5, 7, 9, 19, 20, 23], "t_k": 7, "tab": [1, 5, 7, 19, 20], "tag": [2, 22], "take": [2, 4, 20, 23], "target": 15, "task": [1, 4, 5, 19], "team": [2, 5], "technic": [1, 4, 5, 7], "techniqu": 8, "telescop": 15, "tell": [4, 17], "temp_c": 7, "temp_k": 7, "temperatur": 7, "templat": 22, "tempor": 7, "temporari": 19, "term": 20, "termin": [9, 10, 19, 20, 23], "test": [7, 9, 10, 11, 13, 21], "test_": 17, "test_add": 17, "test_force_between_planet": 17, "text": [9, 21], "than": [10, 20, 21, 22, 23], "thank": 5, "thei": [5, 19, 20, 21, 22], "them": [1, 4, 5, 9, 17, 19, 21], "theme": 7, "themselv": 2, "therefor": 9, "thesi": 21, "thi": [1, 4, 5, 6, 7, 8, 9, 10, 11, 17, 19, 21, 22, 23], "thing": [5, 19, 20, 22], "think": 5, "those": 23, "thread": 23, "three": [7, 19, 20, 23], "thresh": 15, "threshold": [7, 15], "threshold_otsu": 15, "through": [1, 4, 5, 9, 15], "thumb": 7, "time": [2, 4, 7, 8, 17, 19, 20, 21, 23], "timeit": 12, "timelin": 20, "tini": [1, 7], "titl": 5, "toctre": 7, "togeth": [1, 2, 9, 12, 15, 19, 20, 22], "too": [7, 20, 22], "tool": [7, 9, 15, 20, 21, 23], "top": [5, 7, 19, 20, 23], "topic": [14, 15], "total": 23, "toward": [1, 9, 17, 20], "track": [5, 9, 19, 20, 22, 23], "tradit": 11, "trajectori": 8, "translat": 17, "travers": 17, "tri": [21, 23], "trick": 19, "troubleshoot": [5, 23], "true": [7, 15, 17], "truli": 23, "trust": [9, 20], "try": [4, 5, 7, 9, 17, 19, 20, 21, 23], "tune": 2, "tupl": 15, "turn": [9, 18, 21], "tutori": [7, 19], "two": [1, 4, 7, 9, 20, 21, 23], "txt": [9, 11, 15], "type": [5, 15, 20], "typic": [2, 9, 22], "typo": 1, "typographi": 7, "u": [5, 17, 19, 20, 23], "ubuntu": [7, 17], "ugli": 22, "ullamco": 7, "under": [2, 15, 20, 23], "understand": [1, 8, 19, 20], "understood": 22, "undo": 22, "unfinish": [22, 23], "unfortun": 20, "uniqu": [2, 19, 20], "unit": [8, 22], "univers": 21, "unless": 23, "unrel": 22, "unsur": [4, 19, 22], "untest": 22, "until": 5, "unwatch": 5, "up": [4, 5, 9, 15, 22, 23], "updat": [1, 2, 22], "upload": 23, "upper": 19, "upstream": [5, 20], "url": [4, 5, 19, 20, 23], "us": [1, 2, 4, 5, 7, 10, 11, 13, 15, 17, 19, 21, 23], "usag": [9, 20], "user": [4, 5, 19, 20, 23], "usernam": [4, 5, 19, 20], "usual": 20, "ut": 7, "uv": 11, "v": [5, 9, 14, 19, 23], "v1": [7, 17, 19], "v4": [7, 17], "valu": [5, 8, 15], "variabl": 20, "ve": 5, "velit": 7, "veloc": 8, "veniam": 7, "venv": 11, "verbos": [4, 5], "veri": [1, 4, 5, 7, 19, 20, 22, 23], "verifi": [4, 5, 23], "version": [2, 7, 9, 17, 19, 20, 23], "version1": 19, "version2": 19, "via": [5, 9, 23], "view": [1, 4, 5, 7, 19, 20], "virtual": [7, 9], "visibl": [5, 20], "visit": 7, "viztrac": 12, "volupt": 7, "vtune": 12, "vultur": [10, 11, 15], "vx": 8, "vy": 8, "vz": 8, "w": [7, 15], "wa": [4, 5, 6, 7, 20, 21, 22], "wai": [1, 4, 5, 7, 15, 17, 19, 20, 22], "walk": [4, 5], "want": [1, 4, 5, 7, 15, 17, 19, 20, 21, 23], "warm": 7, "watch": 5, "we": [1, 2, 4, 5, 7, 10, 12, 13, 15, 17, 19, 20, 22, 23], "web": [1, 5, 7, 23], "websit": [7, 21, 23], "welcom": 9, "were": [5, 8], "what": [1, 4, 6, 8, 9, 10, 19, 20, 23], "whatthecommit": 22, "wheel": 7, "when": [2, 4, 5, 7, 10, 17, 19, 21, 22], "where": [2, 4, 5, 10, 11, 19, 20, 21, 22], "wherea": 2, "whether": [2, 4, 8, 17], "which": [1, 2, 4, 5, 7, 9, 17, 19, 21, 22, 23], "white": 15, "who": [4, 5], "whole": [1, 2, 17, 20], "why": [4, 22], "window": [5, 20], "wish": [4, 5, 15], "within": [0, 2, 9], "without": [1, 4, 5, 7, 20, 21, 23], "wonder": 4, "word": [17, 21], "work": [1, 2, 4, 5, 7, 8, 9, 17, 19, 21, 23], "workflow": [4, 7, 9, 17], "workshop": [4, 5, 6, 7, 9, 15, 17, 20, 21], "would": [2, 4, 15, 17, 20, 22], "write": [2, 4, 5, 7, 14, 15, 23], "written": 8, "wrong": 5, "wrote": 9, "x": 15, "y": 15, "ye": [20, 23], "year": 22, "yet": [5, 17, 19, 23], "yml": [7, 9, 10, 17], "you": [1, 2, 4, 5, 9, 17, 21, 22, 23], "your": [1, 2, 5, 8, 9, 10, 14, 17, 18, 19, 20, 22], "yourself": 23, "zenodo": [9, 13, 23], "zip": 21, "zola": 7, "zombi": 7, "zsh": 10}, "titles": ["Collaborative version control and code review", "Practicing code review", "Concepts around collaboration", "Conclict resolution, rebasing, and organizational strategies", "How to contribute changes to repositories that belong to others", "Collaborating within the same repository", "Credit", "Code documentation", "Example project: Simulating the motion of planets", "Reproducible research software development using Python", "Conda environment", "Virtual environment", "Profiling memory and CPU usage", "How to release and publish your code", "Concepts in refactoring and modular code design", "Demo: From a script towards a workflow", "How to make the project more reusable", "Automated testing", "Introduction to version control with Git and GitHub", "Creating branches and commits", "Forking, cloning, and browsing", "Motivation", "Practical advice: How much Git is necessary?", "Optional: How to turn your project to a Git repo and share it"], "titleterms": {"": 4, "1": [5, 9, 19, 20], "2": [5, 9, 19, 20], "20": [19, 20, 23], "25": [1, 4, 5], "3": [5, 9, 19, 20], "4": [5, 19, 20], "5": [5, 19, 20], "6": [5, 19, 20], "7": [5, 19], "8": 19, "And": 5, "If": 9, "In": 7, "about": [7, 21, 22], "action": [7, 17], "ad": [5, 17], "address": 1, "advic": 22, "after": 15, "again": 19, "alert": 15, "all": 22, "allow": [7, 20], "an": [5, 20], "answer": 17, "api": 7, "approach": 4, "ar": 20, "arbitrari": 19, "around": 2, "ask": 1, "auto": 7, "autom": [4, 17], "avoid": 22, "back": 21, "background": 19, "basic": 20, "befor": [10, 11], "belong": 4, "between": 2, "big": 9, "branch": [2, 5, 19, 21, 22], "brows": [19, 20], "build": 7, "can": [4, 20], "chang": [1, 2, 4, 5], "checklist": 7, "choos": 10, "clone": [2, 20], "code": [0, 1, 7, 13, 14, 20, 21], "collabor": [0, 2, 4, 5, 21], "command": [15, 20, 22], "comment": 1, "commit": [2, 19, 20, 22], "compar": [19, 20], "complex": 22, "concept": [2, 14], "conclict": 3, "conda": 10, "confus": 7, "contribut": 4, "control": [0, 18], "copi": 20, "coupl": 4, "cpu": 12, "creat": [5, 10, 11, 19, 20], "credit": 6, "cross": 5, "dai": 9, "defin": 15, "demo": [7, 15], "demonstr": 21, "design": 14, "develop": 9, "discuss": [1, 4, 17, 21], "di\u00e1taxi": 7, "do": 21, "docstr": 7, "document": 7, "don": 4, "draft": 5, "each": [20, 22], "els": 17, "end": 17, "enough": 7, "environ": [10, 11], "exampl": 8, "exercis": [1, 4, 5, 7, 17, 19, 20, 23], "exist": 20, "featur": 21, "file": [19, 20], "focu": 8, "fork": [2, 4, 20], "from": [4, 15], "futur": 7, "gener": 7, "git": [18, 22, 23], "github": [5, 7, 17, 18, 20, 23], "gitlab": 23, "goal": [8, 9], "graph": 20, "have": [4, 9], "help": [1, 4], "hint": 5, "histori": 20, "how": [1, 4, 5, 7, 13, 16, 17, 20, 22, 23], "i": [4, 5, 7, 17, 22, 23], "idea": 4, "includ": 20, "instantan": 20, "instructor": [7, 8, 17, 20], "interfac": 15, "introduct": 18, "issu": 5, "just": 19, "keep": 21, "keypoint": 7, "languag": 7, "larg": 22, "last": 20, "learn": 8, "left": 9, "level": 22, "like": 21, "line": [15, 20, 22], "local": 4, "longer": 17, "made": 19, "main": [5, 19], "make": [5, 16], "manag": 10, "mani": 7, "markdown": 7, "memori": 12, "merg": 21, "messag": 22, "min": [1, 4, 5, 19, 20, 23], "modif": 20, "modifi": [1, 5, 19, 20], "modular": 14, "more": [7, 16], "motion": 8, "motiv": [4, 17, 21], "much": 22, "my": 4, "myst": 7, "necessari": 22, "need": [7, 21], "network": 20, "new": [5, 19], "note": [7, 8, 17, 20], "object": [2, 7, 17, 19, 20, 21, 23], "often": [7, 17], "open": [4, 5], "option": [7, 23], "organiz": 3, "other": 4, "peopl": 4, "permiss": 4, "pictur": 9, "plan": 15, "planet": 8, "point": 15, "posit": 20, "possibl": [15, 17], "practic": [1, 19, 22], "prepar": [1, 4, 5, 7, 9, 20], "prerequisit": 9, "profil": 12, "project": [8, 16, 20, 22, 23], "protect": 5, "publish": [13, 23], "pull": [1, 4, 5], "push": [4, 5], "put": 23, "pytest": 17, "python": [7, 9], "read": 7, "readm": 7, "rebas": 3, "recip": 5, "recoveri": 4, "refactor": 14, "referenc": 5, "releas": 13, "renam": 19, "repo": 23, "repositori": [2, 4, 5, 20], "reproduc": 9, "request": [1, 4, 5], "research": 9, "resolut": 3, "restructuredtext": 7, "reusabl": 16, "review": [0, 1, 5], "roll": 21, "rough": 15, "rule": 15, "run": 8, "same": [4, 5], "schedul": 9, "script": 15, "search": 20, "self": 7, "set": 7, "share": [20, 23], "should": 22, "simul": 8, "singl": 20, "snakemak": 15, "snapshot": 21, "softwar": [9, 23], "solut": [5, 15, 19, 20, 21], "some": 15, "sphinx": 7, "spoiler": 15, "stage": 22, "start": [15, 17], "statu": 22, "strategi": 3, "suggest": 1, "summari": [1, 4, 5, 19, 20], "switch": 19, "synchron": 2, "t": 4, "tag": 19, "talk": 21, "taught": 17, "test": [4, 17], "than": 7, "thi": [15, 20], "through": [19, 20], "time": [9, 22], "tool": 10, "toward": [4, 5, 15], "track": 21, "turn": 23, "two": 19, "typic": 21, "unit": 17, "up": 7, "updat": 4, "upstream": 4, "us": [9, 20, 22], "usag": 12, "v": [7, 20], "version": [0, 18, 21], "virtual": [10, 11], "walk": [19, 20], "we": [8, 9, 21], "what": [5, 7, 17, 21, 22], "when": 20, "where": [7, 17], "which": [15, 20], "who": 20, "why": [7, 21], "within": [4, 5], "word": 20, "work": [15, 20, 22], "workflow": 15, "write": 22, "you": [7, 10, 11, 19, 20], "your": [4, 7, 13, 23], "yourself": 20}}) \ No newline at end of file diff --git a/branch/main/singlehtml/_images/add-suggestion.png b/branch/main/singlehtml/_images/add-suggestion.png new file mode 100644 index 0000000..0a28544 Binary files /dev/null and b/branch/main/singlehtml/_images/add-suggestion.png differ diff --git a/branch/main/singlehtml/_images/all-checks-failed.png b/branch/main/singlehtml/_images/all-checks-failed.png new file mode 100644 index 0000000..5175684 Binary files /dev/null and b/branch/main/singlehtml/_images/all-checks-failed.png differ diff --git a/branch/main/singlehtml/_images/animation.mp4 b/branch/main/singlehtml/_images/animation.mp4 new file mode 100644 index 0000000..f037fd4 Binary files /dev/null and b/branch/main/singlehtml/_images/animation.mp4 differ diff --git a/branch/main/singlehtml/_images/bare-repository.png b/branch/main/singlehtml/_images/bare-repository.png new file mode 100644 index 0000000..45286f9 Binary files /dev/null and b/branch/main/singlehtml/_images/bare-repository.png differ diff --git a/branch/main/singlehtml/_images/branches.png b/branch/main/singlehtml/_images/branches.png new file mode 100644 index 0000000..2464b1c Binary files /dev/null and b/branch/main/singlehtml/_images/branches.png differ diff --git a/branch/main/singlehtml/_images/check-details.png b/branch/main/singlehtml/_images/check-details.png new file mode 100644 index 0000000..456a247 Binary files /dev/null and b/branch/main/singlehtml/_images/check-details.png differ diff --git a/branch/main/singlehtml/_images/clone-address.png b/branch/main/singlehtml/_images/clone-address.png new file mode 100644 index 0000000..abdbd85 Binary files /dev/null and b/branch/main/singlehtml/_images/clone-address.png differ diff --git a/branch/main/singlehtml/_images/clone-of-fork.png b/branch/main/singlehtml/_images/clone-of-fork.png new file mode 100644 index 0000000..de1f78d Binary files /dev/null and b/branch/main/singlehtml/_images/clone-of-fork.png differ diff --git a/branch/main/singlehtml/_images/clone.png b/branch/main/singlehtml/_images/clone.png new file mode 100644 index 0000000..ad948ba Binary files /dev/null and b/branch/main/singlehtml/_images/clone.png differ diff --git a/branch/main/singlehtml/_images/comment.png b/branch/main/singlehtml/_images/comment.png new file mode 100644 index 0000000..6ae948c Binary files /dev/null and b/branch/main/singlehtml/_images/comment.png differ diff --git a/branch/main/singlehtml/_images/commit-suggestion.png b/branch/main/singlehtml/_images/commit-suggestion.png new file mode 100644 index 0000000..4ec4bb6 Binary files /dev/null and b/branch/main/singlehtml/_images/commit-suggestion.png differ diff --git a/branch/main/singlehtml/_images/create-repository-with-readme.png b/branch/main/singlehtml/_images/create-repository-with-readme.png new file mode 100644 index 0000000..2411834 Binary files /dev/null and b/branch/main/singlehtml/_images/create-repository-with-readme.png differ diff --git a/branch/main/singlehtml/_images/create-repository.png b/branch/main/singlehtml/_images/create-repository.png new file mode 100644 index 0000000..80fb99c Binary files /dev/null and b/branch/main/singlehtml/_images/create-repository.png differ diff --git a/branch/main/singlehtml/_images/draft-pr-wip.png b/branch/main/singlehtml/_images/draft-pr-wip.png new file mode 100644 index 0000000..12181d5 Binary files /dev/null and b/branch/main/singlehtml/_images/draft-pr-wip.png differ diff --git a/branch/main/singlehtml/_images/draft-pr.png b/branch/main/singlehtml/_images/draft-pr.png new file mode 100644 index 0000000..2099349 Binary files /dev/null and b/branch/main/singlehtml/_images/draft-pr.png differ diff --git a/branch/main/singlehtml/_images/files-changed.png b/branch/main/singlehtml/_images/files-changed.png new file mode 100644 index 0000000..7b58a22 Binary files /dev/null and b/branch/main/singlehtml/_images/files-changed.png differ diff --git a/branch/main/singlehtml/_images/fork-after-update.png b/branch/main/singlehtml/_images/fork-after-update.png new file mode 100644 index 0000000..38fc8c6 Binary files /dev/null and b/branch/main/singlehtml/_images/fork-after-update.png differ diff --git a/branch/main/singlehtml/_images/fork.png b/branch/main/singlehtml/_images/fork.png new file mode 100644 index 0000000..e658473 Binary files /dev/null and b/branch/main/singlehtml/_images/fork.png differ diff --git a/branch/main/singlehtml/_images/forkandclone.png b/branch/main/singlehtml/_images/forkandclone.png new file mode 100644 index 0000000..d050183 Binary files /dev/null and b/branch/main/singlehtml/_images/forkandclone.png differ diff --git a/branch/main/singlehtml/_images/gophers.png b/branch/main/singlehtml/_images/gophers.png new file mode 100644 index 0000000..741406b Binary files /dev/null and b/branch/main/singlehtml/_images/gophers.png differ diff --git a/branch/main/singlehtml/_images/history.png b/branch/main/singlehtml/_images/history.png new file mode 100644 index 0000000..a3a0d99 Binary files /dev/null and b/branch/main/singlehtml/_images/history.png differ diff --git a/branch/main/singlehtml/_images/issue-number.png b/branch/main/singlehtml/_images/issue-number.png new file mode 100644 index 0000000..be273a2 Binary files /dev/null and b/branch/main/singlehtml/_images/issue-number.png differ diff --git a/branch/main/singlehtml/_images/leave-comment.png b/branch/main/singlehtml/_images/leave-comment.png new file mode 100644 index 0000000..95e33c2 Binary files /dev/null and b/branch/main/singlehtml/_images/leave-comment.png differ diff --git a/branch/main/singlehtml/_images/license.png b/branch/main/singlehtml/_images/license.png new file mode 100644 index 0000000..ec5cf85 Binary files /dev/null and b/branch/main/singlehtml/_images/license.png differ diff --git a/branch/main/singlehtml/_images/network.png b/branch/main/singlehtml/_images/network.png new file mode 100644 index 0000000..26a6d63 Binary files /dev/null and b/branch/main/singlehtml/_images/network.png differ diff --git a/branch/main/singlehtml/_images/new-repository.png b/branch/main/singlehtml/_images/new-repository.png new file mode 100644 index 0000000..aff7677 Binary files /dev/null and b/branch/main/singlehtml/_images/new-repository.png differ diff --git a/branch/main/singlehtml/_images/owl.png b/branch/main/singlehtml/_images/owl.png new file mode 100644 index 0000000..7f90e9f Binary files /dev/null and b/branch/main/singlehtml/_images/owl.png differ diff --git a/branch/main/singlehtml/_images/pull-request-form.png b/branch/main/singlehtml/_images/pull-request-form.png new file mode 100644 index 0000000..c6c6af8 Binary files /dev/null and b/branch/main/singlehtml/_images/pull-request-form.png differ diff --git a/branch/main/singlehtml/_images/sharing.png b/branch/main/singlehtml/_images/sharing.png new file mode 100644 index 0000000..8368d6b Binary files /dev/null and b/branch/main/singlehtml/_images/sharing.png differ diff --git a/branch/main/singlehtml/_images/stars.png b/branch/main/singlehtml/_images/stars.png new file mode 100644 index 0000000..f4c5cf7 Binary files /dev/null and b/branch/main/singlehtml/_images/stars.png differ diff --git a/branch/main/singlehtml/_images/sync-fork.png b/branch/main/singlehtml/_images/sync-fork.png new file mode 100644 index 0000000..848a054 Binary files /dev/null and b/branch/main/singlehtml/_images/sync-fork.png differ diff --git a/branch/main/singlehtml/_images/unwatch.png b/branch/main/singlehtml/_images/unwatch.png new file mode 100644 index 0000000..020a6c9 Binary files /dev/null and b/branch/main/singlehtml/_images/unwatch.png differ diff --git a/branch/main/singlehtml/_images/upload-files.png b/branch/main/singlehtml/_images/upload-files.png new file mode 100644 index 0000000..fd3ef8c Binary files /dev/null and b/branch/main/singlehtml/_images/upload-files.png differ diff --git a/branch/main/singlehtml/_images/vscode-authorize.png b/branch/main/singlehtml/_images/vscode-authorize.png new file mode 100644 index 0000000..4d45dc2 Binary files /dev/null and b/branch/main/singlehtml/_images/vscode-authorize.png differ diff --git a/branch/main/singlehtml/_images/vscode-publish-branch.png b/branch/main/singlehtml/_images/vscode-publish-branch.png new file mode 100644 index 0000000..323ffb2 Binary files /dev/null and b/branch/main/singlehtml/_images/vscode-publish-branch.png differ diff --git a/branch/main/singlehtml/_images/vscode-publish-to-github1.png b/branch/main/singlehtml/_images/vscode-publish-to-github1.png new file mode 100644 index 0000000..883e756 Binary files /dev/null and b/branch/main/singlehtml/_images/vscode-publish-to-github1.png differ diff --git a/branch/main/singlehtml/_images/vscode-publish-to-github2.png b/branch/main/singlehtml/_images/vscode-publish-to-github2.png new file mode 100644 index 0000000..d0055db Binary files /dev/null and b/branch/main/singlehtml/_images/vscode-publish-to-github2.png differ diff --git a/branch/main/singlehtml/_images/vscode-publish-to-github3.png b/branch/main/singlehtml/_images/vscode-publish-to-github3.png new file mode 100644 index 0000000..0f68d0e Binary files /dev/null and b/branch/main/singlehtml/_images/vscode-publish-to-github3.png differ diff --git a/branch/main/singlehtml/_images/vscode-start.png b/branch/main/singlehtml/_images/vscode-start.png new file mode 100644 index 0000000..e783080 Binary files /dev/null and b/branch/main/singlehtml/_images/vscode-start.png differ diff --git a/branch/main/singlehtml/_static/_sphinx_javascript_frameworks_compat.js b/branch/main/singlehtml/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000..8141580 --- /dev/null +++ b/branch/main/singlehtml/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/branch/main/singlehtml/_static/basic.css b/branch/main/singlehtml/_static/basic.css new file mode 100644 index 0000000..f316efc --- /dev/null +++ b/branch/main/singlehtml/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/branch/main/singlehtml/_static/check-solid.svg b/branch/main/singlehtml/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/branch/main/singlehtml/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/branch/main/singlehtml/_static/clipboard.min.js b/branch/main/singlehtml/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/branch/main/singlehtml/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/branch/main/singlehtml/_static/copybutton.css b/branch/main/singlehtml/_static/copybutton.css new file mode 100644 index 0000000..40eafe5 --- /dev/null +++ b/branch/main/singlehtml/_static/copybutton.css @@ -0,0 +1,93 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +.highlight:hover button.copybtn { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/branch/main/singlehtml/_static/copybutton.js b/branch/main/singlehtml/_static/copybutton.js new file mode 100644 index 0000000..f3ecd03 --- /dev/null +++ b/branch/main/singlehtml/_static/copybutton.js @@ -0,0 +1,241 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copié dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for two seconds, then changes it back +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000) + setTimeout(() => el.classList.remove('success'), 2000) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, 2000) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos, .gp'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/branch/main/singlehtml/_static/copybutton_funcs.js b/branch/main/singlehtml/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/branch/main/singlehtml/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/branch/main/singlehtml/_static/css/badge_only.css b/branch/main/singlehtml/_static/css/badge_only.css new file mode 100644 index 0000000..c718cee --- /dev/null +++ b/branch/main/singlehtml/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff2 b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff2 b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.eot b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.svg b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.ttf b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.woff b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.woff2 b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-bold-italic.woff b/branch/main/singlehtml/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-bold-italic.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-bold-italic.woff2 b/branch/main/singlehtml/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-bold.woff b/branch/main/singlehtml/_static/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-bold.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-bold.woff2 b/branch/main/singlehtml/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-bold.woff2 differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-normal-italic.woff b/branch/main/singlehtml/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-normal-italic.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-normal-italic.woff2 b/branch/main/singlehtml/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-normal.woff b/branch/main/singlehtml/_static/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-normal.woff differ diff --git a/branch/main/singlehtml/_static/css/fonts/lato-normal.woff2 b/branch/main/singlehtml/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/branch/main/singlehtml/_static/css/fonts/lato-normal.woff2 differ diff --git a/branch/main/singlehtml/_static/css/theme.css b/branch/main/singlehtml/_static/css/theme.css new file mode 100644 index 0000000..19a446a --- /dev/null +++ b/branch/main/singlehtml/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/branch/main/singlehtml/_static/doctools.js b/branch/main/singlehtml/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/branch/main/singlehtml/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/branch/main/singlehtml/_static/documentation_options.js b/branch/main/singlehtml/_static/documentation_options.js new file mode 100644 index 0000000..89003c6 --- /dev/null +++ b/branch/main/singlehtml/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'singlehtml', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/branch/main/singlehtml/_static/file.png b/branch/main/singlehtml/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/branch/main/singlehtml/_static/file.png differ diff --git a/branch/main/singlehtml/_static/jquery.js b/branch/main/singlehtml/_static/jquery.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/branch/main/singlehtml/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/branch/main/singlehtml/_static/js/html5shiv.min.js b/branch/main/singlehtml/_static/js/html5shiv.min.js new file mode 100644 index 0000000..cd1c674 --- /dev/null +++ b/branch/main/singlehtml/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/branch/main/singlehtml/_static/js/theme.js b/branch/main/singlehtml/_static/js/theme.js new file mode 100644 index 0000000..1fddb6e --- /dev/null +++ b/branch/main/singlehtml/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/branch/main/singlehtml/_static/minipres.js b/branch/main/singlehtml/_static/minipres.js new file mode 100644 index 0000000..ad11c87 --- /dev/null +++ b/branch/main/singlehtml/_static/minipres.js @@ -0,0 +1,223 @@ +// Add goTo method to elements +// http://stackoverflow.com/questions/4801655/how-to-go-to-a-specific-element-on-page +(function($) { + $.fn.goTo = function() { + $('html, body').animate({ + scrollTop: $(this).offset().top //+ 'px' + }, 'fast'); + return this; // for chaining... + } +})(jQuery); + +// NO good way to do this!. Copy a hack from here +// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript +// https://stackoverflow.com/a/2880929 +var urlParams; +(window.onpopstate = function () { + var match, + pl = /\+/g, // Regex for replacing addition symbol with a space + search = /([^&=]+)=?([^&]*)/g, + decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, + query = window.location.search.substring(1); + urlParams = {}; + while (match = search.exec(query)) + urlParams[decode(match[1])] = decode(match[2]); +})(); + +// Select heading levels +var maxHeading = urlParams['h'] +if (maxHeading === undefined) maxHeading = 2 +var headingLevels = []; +for (h=2 ; h (sections.length-1) ) { + // if we would scroll past bottom, or above top, do nothing + return; + } + + console.log('xxxxxx'); + var targetSection = sections[targetPos]; + console.log(targetSection, typeof(targetSection)); + + // Return targetSection top and height + var secProperties = section_top_and_height(targetSection); + var top = secProperties['top']; + var height = secProperties['height'] + var win_height = window.innerHeight; + //console.info(top, height, win_height) + + var scroll_to = 0; + if (height >= win_height || height == 0) { + scroll_to = top; + } else { + scroll_to = top - (win_height-height)/3.; + } + //console.info(top, height, win_height, scroll_to) + + $('html, body').animate({ + scrollTop: scroll_to //+ 'px' + }, 'fast'); + +} + + +function minipres() { + /* Enable the minipres mode: + - call the hide() function + - set up the scrolling listener + */ + document.addEventListener('keydown', function (event) { + switch(event.which) { + case 37: // left + switch_slide(-1); + event.preventDefault(); + return false; + break; + //case 38: // up + case 39: // right + switch_slide(+1); + event.preventDefault(); + return false; + break; + //case 40: // down + default: + return; // exit this handler for other keys + } + }, true) + + hide() + + // Increase space between sections + //$("div .section").css('margin-bottom', '50%'); + $(sectionSelector).css('margin-top', '50%'); + + // Reduce size/color of other sections + if (hiddenSectionSelector.length > 0) { + var hideNodes = $(hiddenSectionSelector); + console.log(typeof hideNodes, hideNodes); + for (node in hideNodes) { + console.log("a", typeof node, node); + node = hideNodes[node]; // what's right way to iterate values? + console.log("b", typeof node, node); + if (node.parentNode && node.parentNode.className == "section") { + node = node.parentNode; + console.log("c", typeof node, node); + //node.css['transform'] = 'scale(.5)'; + //node.css['transform-origin'] = 'top center'; + $(node).css('color', 'lightgrey'); + //$(node).css('font-size', '20%'); + //$(node).css('visibility', 'collapse'); + //ntahousnatouhasno; + } + } + } +} + +function hide() { + /* Hide all non-essential elements on the page + */ + + // This is for sphinx_rst_theme and readthedocs + $(".wy-nav-side").remove(); + $(".wy-nav-content-wrap").css('margin-left', 0); + $('.rst-versions').remove(); // readthedocs version selector + + // Add other formats here. +} + + +var slideshow = minipres; + +if (window.location.search.match(/[?&](minipres|slideshow|pres)([=&]|$)/) ) { + //minipres() + window.addEventListener("load", minipres); +} else if (window.location.search.match(/[?&](plain)([=&]|$)/) ) { + window.addEventListener("load", hide); +} diff --git a/branch/main/singlehtml/_static/minus.png b/branch/main/singlehtml/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/branch/main/singlehtml/_static/minus.png differ diff --git a/branch/main/singlehtml/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css b/branch/main/singlehtml/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css new file mode 100644 index 0000000..3356631 --- /dev/null +++ b/branch/main/singlehtml/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css @@ -0,0 +1,2342 @@ +/* Variables */ +:root { + --mystnb-source-bg-color: #f7f7f7; + --mystnb-stdout-bg-color: #fcfcfc; + --mystnb-stderr-bg-color: #fdd; + --mystnb-traceback-bg-color: #fcfcfc; + --mystnb-source-border-color: #ccc; + --mystnb-source-margin-color: green; + --mystnb-stdout-border-color: #f7f7f7; + --mystnb-stderr-border-color: #f7f7f7; + --mystnb-traceback-border-color: #ffd6d6; + --mystnb-hide-prompt-opacity: 70%; + --mystnb-source-border-radius: .4em; + --mystnb-source-border-width: 1px; +} + +/* Whole cell */ +div.container.cell { + padding-left: 0; + margin-bottom: 1em; +} + +/* Removing all background formatting so we can control at the div level */ +.cell_input div.highlight, +.cell_output pre, +.cell_input pre, +.cell_output .output { + border: none; + box-shadow: none; +} + +.cell_output .output pre, +.cell_input pre { + margin: 0px; +} + +/* Input cells */ +div.cell div.cell_input, +div.cell details.above-input>summary { + padding-left: 0em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + background-color: var(--mystnb-source-bg-color); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; + border-radius: var(--mystnb-source-border-radius); +} + +div.cell_input>div, +div.cell_output div.output>div.highlight { + margin: 0em !important; + border: none !important; +} + +/* All cell outputs */ +.cell_output { + padding-left: 1em; + padding-right: 0em; + margin-top: 1em; +} + +/* Text outputs from cells */ +.cell_output .output.text_plain, +.cell_output .output.traceback, +.cell_output .output.stream, +.cell_output .output.stderr { + margin-top: 1em; + margin-bottom: 0em; + box-shadow: none; +} + +.cell_output .output.text_plain, +.cell_output .output.stream { + background: var(--mystnb-stdout-bg-color); + border: 1px solid var(--mystnb-stdout-border-color); +} + +.cell_output .output.stderr { + background: var(--mystnb-stderr-bg-color); + border: 1px solid var(--mystnb-stderr-border-color); +} + +.cell_output .output.traceback { + background: var(--mystnb-traceback-bg-color); + border: 1px solid var(--mystnb-traceback-border-color); +} + +/* Collapsible cell content */ +div.cell details.above-input div.cell_input { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; +} + +div.cell div.cell_input.above-output-prompt { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +div.cell details.above-input>summary { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; + padding-left: 1em; + margin-bottom: 0; +} + +div.cell details.above-output>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.below-input>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-top: none; + border-bottom-left-radius: var(--mystnb-source-border-radius); + border-bottom-right-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.hide>summary>span { + opacity: var(--mystnb-hide-prompt-opacity); +} + +div.cell details.hide[open]>summary>span.collapsed { + display: none; +} + +div.cell details.hide:not([open])>summary>span.expanded { + display: none; +} + +@keyframes collapsed-fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} +div.cell details.hide[open]>summary~* { + -moz-animation: collapsed-fade-in 0.3s ease-in-out; + -webkit-animation: collapsed-fade-in 0.3s ease-in-out; + animation: collapsed-fade-in 0.3s ease-in-out; +} + +/* Math align to the left */ +.cell_output .MathJax_Display { + text-align: left !important; +} + +/* Pandas tables. Pulled from the Jupyter / nbsphinx CSS */ +div.cell_output table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 1em; + table-layout: fixed; +} + +div.cell_output thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} + +div.cell_output tr, +div.cell_output th, +div.cell_output td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} + +div.cell_output th { + font-weight: bold; +} + +div.cell_output tbody tr:nth-child(odd) { + background: #f5f5f5; +} + +div.cell_output tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + +/** source code line numbers **/ +span.linenos { + opacity: 0.5; +} + +/* Inline text from `paste` operation */ + +span.pasted-text { + font-weight: bold; +} + +span.pasted-inline img { + max-height: 2em; +} + +tbody span.pasted-inline img { + max-height: none; +} + +/* Font colors for translated ANSI escape sequences +Color values are copied from Jupyter Notebook +https://github.com/jupyter/notebook/blob/52581f8eda9b319eb0390ac77fe5903c38f81e3e/notebook/static/notebook/less/ansicolors.less#L14-L21 +Background colors from +https://nbsphinx.readthedocs.io/en/latest/code-cells.html#ANSI-Colors +*/ +div.highlight .-Color-Bold { + font-weight: bold; +} + +div.highlight .-Color[class*=-Black] { + color: #3E424D +} + +div.highlight .-Color[class*=-Red] { + color: #E75C58 +} + +div.highlight .-Color[class*=-Green] { + color: #00A250 +} + +div.highlight .-Color[class*=-Yellow] { + color: #DDB62B +} + +div.highlight .-Color[class*=-Blue] { + color: #208FFB +} + +div.highlight .-Color[class*=-Magenta] { + color: #D160C4 +} + +div.highlight .-Color[class*=-Cyan] { + color: #60C6C8 +} + +div.highlight .-Color[class*=-White] { + color: #C5C1B4 +} + +div.highlight .-Color[class*=-BGBlack] { + background-color: #3E424D +} + +div.highlight .-Color[class*=-BGRed] { + background-color: #E75C58 +} + +div.highlight .-Color[class*=-BGGreen] { + background-color: #00A250 +} + +div.highlight .-Color[class*=-BGYellow] { + background-color: #DDB62B +} + +div.highlight .-Color[class*=-BGBlue] { + background-color: #208FFB +} + +div.highlight .-Color[class*=-BGMagenta] { + background-color: #D160C4 +} + +div.highlight .-Color[class*=-BGCyan] { + background-color: #60C6C8 +} + +div.highlight .-Color[class*=-BGWhite] { + background-color: #C5C1B4 +} + +/* Font colors for 8-bit ANSI */ + +div.highlight .-Color[class*=-C0] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC0] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C1] { + color: #800000 +} + +div.highlight .-Color[class*=-BGC1] { + background-color: #800000 +} + +div.highlight .-Color[class*=-C2] { + color: #008000 +} + +div.highlight .-Color[class*=-BGC2] { + background-color: #008000 +} + +div.highlight .-Color[class*=-C3] { + color: #808000 +} + +div.highlight .-Color[class*=-BGC3] { + background-color: #808000 +} + +div.highlight .-Color[class*=-C4] { + color: #000080 +} + +div.highlight .-Color[class*=-BGC4] { + background-color: #000080 +} + +div.highlight .-Color[class*=-C5] { + color: #800080 +} + +div.highlight .-Color[class*=-BGC5] { + background-color: #800080 +} + +div.highlight .-Color[class*=-C6] { + color: #008080 +} + +div.highlight .-Color[class*=-BGC6] { + background-color: #008080 +} + +div.highlight .-Color[class*=-C7] { + color: #C0C0C0 +} + +div.highlight .-Color[class*=-BGC7] { + background-color: #C0C0C0 +} + +div.highlight .-Color[class*=-C8] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC8] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C9] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC9] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C10] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC10] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C11] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC11] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C12] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC12] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C13] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC13] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C14] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC14] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C15] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC15] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C16] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC16] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C17] { + color: #00005F +} + +div.highlight .-Color[class*=-BGC17] { + background-color: #00005F +} + +div.highlight .-Color[class*=-C18] { + color: #000087 +} + +div.highlight .-Color[class*=-BGC18] { + background-color: #000087 +} + +div.highlight .-Color[class*=-C19] { + color: #0000AF +} + +div.highlight .-Color[class*=-BGC19] { + background-color: #0000AF +} + +div.highlight .-Color[class*=-C20] { + color: #0000D7 +} + +div.highlight .-Color[class*=-BGC20] { + background-color: #0000D7 +} + +div.highlight .-Color[class*=-C21] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC21] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C22] { + color: #005F00 +} + +div.highlight .-Color[class*=-BGC22] { + background-color: #005F00 +} + +div.highlight .-Color[class*=-C23] { + color: #005F5F +} + +div.highlight .-Color[class*=-BGC23] { + background-color: #005F5F +} + +div.highlight .-Color[class*=-C24] { + color: #005F87 +} + +div.highlight .-Color[class*=-BGC24] { + background-color: #005F87 +} + +div.highlight .-Color[class*=-C25] { + color: #005FAF +} + +div.highlight .-Color[class*=-BGC25] { + background-color: #005FAF +} + +div.highlight .-Color[class*=-C26] { + color: #005FD7 +} + +div.highlight .-Color[class*=-BGC26] { + background-color: #005FD7 +} + +div.highlight .-Color[class*=-C27] { + color: #005FFF +} + +div.highlight .-Color[class*=-BGC27] { + background-color: #005FFF +} + +div.highlight .-Color[class*=-C28] { + color: #008700 +} + +div.highlight .-Color[class*=-BGC28] { + background-color: #008700 +} + +div.highlight .-Color[class*=-C29] { + color: #00875F +} + +div.highlight .-Color[class*=-BGC29] { + background-color: #00875F +} + +div.highlight .-Color[class*=-C30] { + color: #008787 +} + +div.highlight .-Color[class*=-BGC30] { + background-color: #008787 +} + +div.highlight .-Color[class*=-C31] { + color: #0087AF +} + +div.highlight .-Color[class*=-BGC31] { + background-color: #0087AF +} + +div.highlight .-Color[class*=-C32] { + color: #0087D7 +} + +div.highlight .-Color[class*=-BGC32] { + background-color: #0087D7 +} + +div.highlight .-Color[class*=-C33] { + color: #0087FF +} + +div.highlight .-Color[class*=-BGC33] { + background-color: #0087FF +} + +div.highlight .-Color[class*=-C34] { + color: #00AF00 +} + +div.highlight .-Color[class*=-BGC34] { + background-color: #00AF00 +} + +div.highlight .-Color[class*=-C35] { + color: #00AF5F +} + +div.highlight .-Color[class*=-BGC35] { + background-color: #00AF5F +} + +div.highlight .-Color[class*=-C36] { + color: #00AF87 +} + +div.highlight .-Color[class*=-BGC36] { + background-color: #00AF87 +} + +div.highlight .-Color[class*=-C37] { + color: #00AFAF +} + +div.highlight .-Color[class*=-BGC37] { + background-color: #00AFAF +} + +div.highlight .-Color[class*=-C38] { + color: #00AFD7 +} + +div.highlight .-Color[class*=-BGC38] { + background-color: #00AFD7 +} + +div.highlight .-Color[class*=-C39] { + color: #00AFFF +} + +div.highlight .-Color[class*=-BGC39] { + background-color: #00AFFF +} + +div.highlight .-Color[class*=-C40] { + color: #00D700 +} + +div.highlight .-Color[class*=-BGC40] { + background-color: #00D700 +} + +div.highlight .-Color[class*=-C41] { + color: #00D75F +} + +div.highlight .-Color[class*=-BGC41] { + background-color: #00D75F +} + +div.highlight .-Color[class*=-C42] { + color: #00D787 +} + +div.highlight .-Color[class*=-BGC42] { + background-color: #00D787 +} + +div.highlight .-Color[class*=-C43] { + color: #00D7AF +} + +div.highlight .-Color[class*=-BGC43] { + background-color: #00D7AF +} + +div.highlight .-Color[class*=-C44] { + color: #00D7D7 +} + +div.highlight .-Color[class*=-BGC44] { + background-color: #00D7D7 +} + +div.highlight .-Color[class*=-C45] { + color: #00D7FF +} + +div.highlight .-Color[class*=-BGC45] { + background-color: #00D7FF +} + +div.highlight .-Color[class*=-C46] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC46] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C47] { + color: #00FF5F +} + +div.highlight .-Color[class*=-BGC47] { + background-color: #00FF5F +} + +div.highlight .-Color[class*=-C48] { + color: #00FF87 +} + +div.highlight .-Color[class*=-BGC48] { + background-color: #00FF87 +} + +div.highlight .-Color[class*=-C49] { + color: #00FFAF +} + +div.highlight .-Color[class*=-BGC49] { + background-color: #00FFAF +} + +div.highlight .-Color[class*=-C50] { + color: #00FFD7 +} + +div.highlight .-Color[class*=-BGC50] { + background-color: #00FFD7 +} + +div.highlight .-Color[class*=-C51] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC51] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C52] { + color: #5F0000 +} + +div.highlight .-Color[class*=-BGC52] { + background-color: #5F0000 +} + +div.highlight .-Color[class*=-C53] { + color: #5F005F +} + +div.highlight .-Color[class*=-BGC53] { + background-color: #5F005F +} + +div.highlight .-Color[class*=-C54] { + color: #5F0087 +} + +div.highlight .-Color[class*=-BGC54] { + background-color: #5F0087 +} + +div.highlight .-Color[class*=-C55] { + color: #5F00AF +} + +div.highlight .-Color[class*=-BGC55] { + background-color: #5F00AF +} + +div.highlight .-Color[class*=-C56] { + color: #5F00D7 +} + +div.highlight .-Color[class*=-BGC56] { + background-color: #5F00D7 +} + +div.highlight .-Color[class*=-C57] { + color: #5F00FF +} + +div.highlight .-Color[class*=-BGC57] { + background-color: #5F00FF +} + +div.highlight .-Color[class*=-C58] { + color: #5F5F00 +} + +div.highlight .-Color[class*=-BGC58] { + background-color: #5F5F00 +} + +div.highlight .-Color[class*=-C59] { + color: #5F5F5F +} + +div.highlight .-Color[class*=-BGC59] { + background-color: #5F5F5F +} + +div.highlight .-Color[class*=-C60] { + color: #5F5F87 +} + +div.highlight .-Color[class*=-BGC60] { + background-color: #5F5F87 +} + +div.highlight .-Color[class*=-C61] { + color: #5F5FAF +} + +div.highlight .-Color[class*=-BGC61] { + background-color: #5F5FAF +} + +div.highlight .-Color[class*=-C62] { + color: #5F5FD7 +} + +div.highlight .-Color[class*=-BGC62] { + background-color: #5F5FD7 +} + +div.highlight .-Color[class*=-C63] { + color: #5F5FFF +} + +div.highlight .-Color[class*=-BGC63] { + background-color: #5F5FFF +} + +div.highlight .-Color[class*=-C64] { + color: #5F8700 +} + +div.highlight .-Color[class*=-BGC64] { + background-color: #5F8700 +} + +div.highlight .-Color[class*=-C65] { + color: #5F875F +} + +div.highlight .-Color[class*=-BGC65] { + background-color: #5F875F +} + +div.highlight .-Color[class*=-C66] { + color: #5F8787 +} + +div.highlight .-Color[class*=-BGC66] { + background-color: #5F8787 +} + +div.highlight .-Color[class*=-C67] { + color: #5F87AF +} + +div.highlight .-Color[class*=-BGC67] { + background-color: #5F87AF +} + +div.highlight .-Color[class*=-C68] { + color: #5F87D7 +} + +div.highlight .-Color[class*=-BGC68] { + background-color: #5F87D7 +} + +div.highlight .-Color[class*=-C69] { + color: #5F87FF +} + +div.highlight .-Color[class*=-BGC69] { + background-color: #5F87FF +} + +div.highlight .-Color[class*=-C70] { + color: #5FAF00 +} + +div.highlight .-Color[class*=-BGC70] { + background-color: #5FAF00 +} + +div.highlight .-Color[class*=-C71] { + color: #5FAF5F +} + +div.highlight .-Color[class*=-BGC71] { + background-color: #5FAF5F +} + +div.highlight .-Color[class*=-C72] { + color: #5FAF87 +} + +div.highlight .-Color[class*=-BGC72] { + background-color: #5FAF87 +} + +div.highlight .-Color[class*=-C73] { + color: #5FAFAF +} + +div.highlight .-Color[class*=-BGC73] { + background-color: #5FAFAF +} + +div.highlight .-Color[class*=-C74] { + color: #5FAFD7 +} + +div.highlight .-Color[class*=-BGC74] { + background-color: #5FAFD7 +} + +div.highlight .-Color[class*=-C75] { + color: #5FAFFF +} + +div.highlight .-Color[class*=-BGC75] { + background-color: #5FAFFF +} + +div.highlight .-Color[class*=-C76] { + color: #5FD700 +} + +div.highlight .-Color[class*=-BGC76] { + background-color: #5FD700 +} + +div.highlight .-Color[class*=-C77] { + color: #5FD75F +} + +div.highlight .-Color[class*=-BGC77] { + background-color: #5FD75F +} + +div.highlight .-Color[class*=-C78] { + color: #5FD787 +} + +div.highlight .-Color[class*=-BGC78] { + background-color: #5FD787 +} + +div.highlight .-Color[class*=-C79] { + color: #5FD7AF +} + +div.highlight .-Color[class*=-BGC79] { + background-color: #5FD7AF +} + +div.highlight .-Color[class*=-C80] { + color: #5FD7D7 +} + +div.highlight .-Color[class*=-BGC80] { + background-color: #5FD7D7 +} + +div.highlight .-Color[class*=-C81] { + color: #5FD7FF +} + +div.highlight .-Color[class*=-BGC81] { + background-color: #5FD7FF +} + +div.highlight .-Color[class*=-C82] { + color: #5FFF00 +} + +div.highlight .-Color[class*=-BGC82] { + background-color: #5FFF00 +} + +div.highlight .-Color[class*=-C83] { + color: #5FFF5F +} + +div.highlight .-Color[class*=-BGC83] { + background-color: #5FFF5F +} + +div.highlight .-Color[class*=-C84] { + color: #5FFF87 +} + +div.highlight .-Color[class*=-BGC84] { + background-color: #5FFF87 +} + +div.highlight .-Color[class*=-C85] { + color: #5FFFAF +} + +div.highlight .-Color[class*=-BGC85] { + background-color: #5FFFAF +} + +div.highlight .-Color[class*=-C86] { + color: #5FFFD7 +} + +div.highlight .-Color[class*=-BGC86] { + background-color: #5FFFD7 +} + +div.highlight .-Color[class*=-C87] { + color: #5FFFFF +} + +div.highlight .-Color[class*=-BGC87] { + background-color: #5FFFFF +} + +div.highlight .-Color[class*=-C88] { + color: #870000 +} + +div.highlight .-Color[class*=-BGC88] { + background-color: #870000 +} + +div.highlight .-Color[class*=-C89] { + color: #87005F +} + +div.highlight .-Color[class*=-BGC89] { + background-color: #87005F +} + +div.highlight .-Color[class*=-C90] { + color: #870087 +} + +div.highlight .-Color[class*=-BGC90] { + background-color: #870087 +} + +div.highlight .-Color[class*=-C91] { + color: #8700AF +} + +div.highlight .-Color[class*=-BGC91] { + background-color: #8700AF +} + +div.highlight .-Color[class*=-C92] { + color: #8700D7 +} + +div.highlight .-Color[class*=-BGC92] { + background-color: #8700D7 +} + +div.highlight .-Color[class*=-C93] { + color: #8700FF +} + +div.highlight .-Color[class*=-BGC93] { + background-color: #8700FF +} + +div.highlight .-Color[class*=-C94] { + color: #875F00 +} + +div.highlight .-Color[class*=-BGC94] { + background-color: #875F00 +} + +div.highlight .-Color[class*=-C95] { + color: #875F5F +} + +div.highlight .-Color[class*=-BGC95] { + background-color: #875F5F +} + +div.highlight .-Color[class*=-C96] { + color: #875F87 +} + +div.highlight .-Color[class*=-BGC96] { + background-color: #875F87 +} + +div.highlight .-Color[class*=-C97] { + color: #875FAF +} + +div.highlight .-Color[class*=-BGC97] { + background-color: #875FAF +} + +div.highlight .-Color[class*=-C98] { + color: #875FD7 +} + +div.highlight .-Color[class*=-BGC98] { + background-color: #875FD7 +} + +div.highlight .-Color[class*=-C99] { + color: #875FFF +} + +div.highlight .-Color[class*=-BGC99] { + background-color: #875FFF +} + +div.highlight .-Color[class*=-C100] { + color: #878700 +} + +div.highlight .-Color[class*=-BGC100] { + background-color: #878700 +} + +div.highlight .-Color[class*=-C101] { + color: #87875F +} + +div.highlight .-Color[class*=-BGC101] { + background-color: #87875F +} + +div.highlight .-Color[class*=-C102] { + color: #878787 +} + +div.highlight .-Color[class*=-BGC102] { + background-color: #878787 +} + +div.highlight .-Color[class*=-C103] { + color: #8787AF +} + +div.highlight .-Color[class*=-BGC103] { + background-color: #8787AF +} + +div.highlight .-Color[class*=-C104] { + color: #8787D7 +} + +div.highlight .-Color[class*=-BGC104] { + background-color: #8787D7 +} + +div.highlight .-Color[class*=-C105] { + color: #8787FF +} + +div.highlight .-Color[class*=-BGC105] { + background-color: #8787FF +} + +div.highlight .-Color[class*=-C106] { + color: #87AF00 +} + +div.highlight .-Color[class*=-BGC106] { + background-color: #87AF00 +} + +div.highlight .-Color[class*=-C107] { + color: #87AF5F +} + +div.highlight .-Color[class*=-BGC107] { + background-color: #87AF5F +} + +div.highlight .-Color[class*=-C108] { + color: #87AF87 +} + +div.highlight .-Color[class*=-BGC108] { + background-color: #87AF87 +} + +div.highlight .-Color[class*=-C109] { + color: #87AFAF +} + +div.highlight .-Color[class*=-BGC109] { + background-color: #87AFAF +} + +div.highlight .-Color[class*=-C110] { + color: #87AFD7 +} + +div.highlight .-Color[class*=-BGC110] { + background-color: #87AFD7 +} + +div.highlight .-Color[class*=-C111] { + color: #87AFFF +} + +div.highlight .-Color[class*=-BGC111] { + background-color: #87AFFF +} + +div.highlight .-Color[class*=-C112] { + color: #87D700 +} + +div.highlight .-Color[class*=-BGC112] { + background-color: #87D700 +} + +div.highlight .-Color[class*=-C113] { + color: #87D75F +} + +div.highlight .-Color[class*=-BGC113] { + background-color: #87D75F +} + +div.highlight .-Color[class*=-C114] { + color: #87D787 +} + +div.highlight .-Color[class*=-BGC114] { + background-color: #87D787 +} + +div.highlight .-Color[class*=-C115] { + color: #87D7AF +} + +div.highlight .-Color[class*=-BGC115] { + background-color: #87D7AF +} + +div.highlight .-Color[class*=-C116] { + color: #87D7D7 +} + +div.highlight .-Color[class*=-BGC116] { + background-color: #87D7D7 +} + +div.highlight .-Color[class*=-C117] { + color: #87D7FF +} + +div.highlight .-Color[class*=-BGC117] { + background-color: #87D7FF +} + +div.highlight .-Color[class*=-C118] { + color: #87FF00 +} + +div.highlight .-Color[class*=-BGC118] { + background-color: #87FF00 +} + +div.highlight .-Color[class*=-C119] { + color: #87FF5F +} + +div.highlight .-Color[class*=-BGC119] { + background-color: #87FF5F +} + +div.highlight .-Color[class*=-C120] { + color: #87FF87 +} + +div.highlight .-Color[class*=-BGC120] { + background-color: #87FF87 +} + +div.highlight .-Color[class*=-C121] { + color: #87FFAF +} + +div.highlight .-Color[class*=-BGC121] { + background-color: #87FFAF +} + +div.highlight .-Color[class*=-C122] { + color: #87FFD7 +} + +div.highlight .-Color[class*=-BGC122] { + background-color: #87FFD7 +} + +div.highlight .-Color[class*=-C123] { + color: #87FFFF +} + +div.highlight .-Color[class*=-BGC123] { + background-color: #87FFFF +} + +div.highlight .-Color[class*=-C124] { + color: #AF0000 +} + +div.highlight .-Color[class*=-BGC124] { + background-color: #AF0000 +} + +div.highlight .-Color[class*=-C125] { + color: #AF005F +} + +div.highlight .-Color[class*=-BGC125] { + background-color: #AF005F +} + +div.highlight .-Color[class*=-C126] { + color: #AF0087 +} + +div.highlight .-Color[class*=-BGC126] { + background-color: #AF0087 +} + +div.highlight .-Color[class*=-C127] { + color: #AF00AF +} + +div.highlight .-Color[class*=-BGC127] { + background-color: #AF00AF +} + +div.highlight .-Color[class*=-C128] { + color: #AF00D7 +} + +div.highlight .-Color[class*=-BGC128] { + background-color: #AF00D7 +} + +div.highlight .-Color[class*=-C129] { + color: #AF00FF +} + +div.highlight .-Color[class*=-BGC129] { + background-color: #AF00FF +} + +div.highlight .-Color[class*=-C130] { + color: #AF5F00 +} + +div.highlight .-Color[class*=-BGC130] { + background-color: #AF5F00 +} + +div.highlight .-Color[class*=-C131] { + color: #AF5F5F +} + +div.highlight .-Color[class*=-BGC131] { + background-color: #AF5F5F +} + +div.highlight .-Color[class*=-C132] { + color: #AF5F87 +} + +div.highlight .-Color[class*=-BGC132] { + background-color: #AF5F87 +} + +div.highlight .-Color[class*=-C133] { + color: #AF5FAF +} + +div.highlight .-Color[class*=-BGC133] { + background-color: #AF5FAF +} + +div.highlight .-Color[class*=-C134] { + color: #AF5FD7 +} + +div.highlight .-Color[class*=-BGC134] { + background-color: #AF5FD7 +} + +div.highlight .-Color[class*=-C135] { + color: #AF5FFF +} + +div.highlight .-Color[class*=-BGC135] { + background-color: #AF5FFF +} + +div.highlight .-Color[class*=-C136] { + color: #AF8700 +} + +div.highlight .-Color[class*=-BGC136] { + background-color: #AF8700 +} + +div.highlight .-Color[class*=-C137] { + color: #AF875F +} + +div.highlight .-Color[class*=-BGC137] { + background-color: #AF875F +} + +div.highlight .-Color[class*=-C138] { + color: #AF8787 +} + +div.highlight .-Color[class*=-BGC138] { + background-color: #AF8787 +} + +div.highlight .-Color[class*=-C139] { + color: #AF87AF +} + +div.highlight .-Color[class*=-BGC139] { + background-color: #AF87AF +} + +div.highlight .-Color[class*=-C140] { + color: #AF87D7 +} + +div.highlight .-Color[class*=-BGC140] { + background-color: #AF87D7 +} + +div.highlight .-Color[class*=-C141] { + color: #AF87FF +} + +div.highlight .-Color[class*=-BGC141] { + background-color: #AF87FF +} + +div.highlight .-Color[class*=-C142] { + color: #AFAF00 +} + +div.highlight .-Color[class*=-BGC142] { + background-color: #AFAF00 +} + +div.highlight .-Color[class*=-C143] { + color: #AFAF5F +} + +div.highlight .-Color[class*=-BGC143] { + background-color: #AFAF5F +} + +div.highlight .-Color[class*=-C144] { + color: #AFAF87 +} + +div.highlight .-Color[class*=-BGC144] { + background-color: #AFAF87 +} + +div.highlight .-Color[class*=-C145] { + color: #AFAFAF +} + +div.highlight .-Color[class*=-BGC145] { + background-color: #AFAFAF +} + +div.highlight .-Color[class*=-C146] { + color: #AFAFD7 +} + +div.highlight .-Color[class*=-BGC146] { + background-color: #AFAFD7 +} + +div.highlight .-Color[class*=-C147] { + color: #AFAFFF +} + +div.highlight .-Color[class*=-BGC147] { + background-color: #AFAFFF +} + +div.highlight .-Color[class*=-C148] { + color: #AFD700 +} + +div.highlight .-Color[class*=-BGC148] { + background-color: #AFD700 +} + +div.highlight .-Color[class*=-C149] { + color: #AFD75F +} + +div.highlight .-Color[class*=-BGC149] { + background-color: #AFD75F +} + +div.highlight .-Color[class*=-C150] { + color: #AFD787 +} + +div.highlight .-Color[class*=-BGC150] { + background-color: #AFD787 +} + +div.highlight .-Color[class*=-C151] { + color: #AFD7AF +} + +div.highlight .-Color[class*=-BGC151] { + background-color: #AFD7AF +} + +div.highlight .-Color[class*=-C152] { + color: #AFD7D7 +} + +div.highlight .-Color[class*=-BGC152] { + background-color: #AFD7D7 +} + +div.highlight .-Color[class*=-C153] { + color: #AFD7FF +} + +div.highlight .-Color[class*=-BGC153] { + background-color: #AFD7FF +} + +div.highlight .-Color[class*=-C154] { + color: #AFFF00 +} + +div.highlight .-Color[class*=-BGC154] { + background-color: #AFFF00 +} + +div.highlight .-Color[class*=-C155] { + color: #AFFF5F +} + +div.highlight .-Color[class*=-BGC155] { + background-color: #AFFF5F +} + +div.highlight .-Color[class*=-C156] { + color: #AFFF87 +} + +div.highlight .-Color[class*=-BGC156] { + background-color: #AFFF87 +} + +div.highlight .-Color[class*=-C157] { + color: #AFFFAF +} + +div.highlight .-Color[class*=-BGC157] { + background-color: #AFFFAF +} + +div.highlight .-Color[class*=-C158] { + color: #AFFFD7 +} + +div.highlight .-Color[class*=-BGC158] { + background-color: #AFFFD7 +} + +div.highlight .-Color[class*=-C159] { + color: #AFFFFF +} + +div.highlight .-Color[class*=-BGC159] { + background-color: #AFFFFF +} + +div.highlight .-Color[class*=-C160] { + color: #D70000 +} + +div.highlight .-Color[class*=-BGC160] { + background-color: #D70000 +} + +div.highlight .-Color[class*=-C161] { + color: #D7005F +} + +div.highlight .-Color[class*=-BGC161] { + background-color: #D7005F +} + +div.highlight .-Color[class*=-C162] { + color: #D70087 +} + +div.highlight .-Color[class*=-BGC162] { + background-color: #D70087 +} + +div.highlight .-Color[class*=-C163] { + color: #D700AF +} + +div.highlight .-Color[class*=-BGC163] { + background-color: #D700AF +} + +div.highlight .-Color[class*=-C164] { + color: #D700D7 +} + +div.highlight .-Color[class*=-BGC164] { + background-color: #D700D7 +} + +div.highlight .-Color[class*=-C165] { + color: #D700FF +} + +div.highlight .-Color[class*=-BGC165] { + background-color: #D700FF +} + +div.highlight .-Color[class*=-C166] { + color: #D75F00 +} + +div.highlight .-Color[class*=-BGC166] { + background-color: #D75F00 +} + +div.highlight .-Color[class*=-C167] { + color: #D75F5F +} + +div.highlight .-Color[class*=-BGC167] { + background-color: #D75F5F +} + +div.highlight .-Color[class*=-C168] { + color: #D75F87 +} + +div.highlight .-Color[class*=-BGC168] { + background-color: #D75F87 +} + +div.highlight .-Color[class*=-C169] { + color: #D75FAF +} + +div.highlight .-Color[class*=-BGC169] { + background-color: #D75FAF +} + +div.highlight .-Color[class*=-C170] { + color: #D75FD7 +} + +div.highlight .-Color[class*=-BGC170] { + background-color: #D75FD7 +} + +div.highlight .-Color[class*=-C171] { + color: #D75FFF +} + +div.highlight .-Color[class*=-BGC171] { + background-color: #D75FFF +} + +div.highlight .-Color[class*=-C172] { + color: #D78700 +} + +div.highlight .-Color[class*=-BGC172] { + background-color: #D78700 +} + +div.highlight .-Color[class*=-C173] { + color: #D7875F +} + +div.highlight .-Color[class*=-BGC173] { + background-color: #D7875F +} + +div.highlight .-Color[class*=-C174] { + color: #D78787 +} + +div.highlight .-Color[class*=-BGC174] { + background-color: #D78787 +} + +div.highlight .-Color[class*=-C175] { + color: #D787AF +} + +div.highlight .-Color[class*=-BGC175] { + background-color: #D787AF +} + +div.highlight .-Color[class*=-C176] { + color: #D787D7 +} + +div.highlight .-Color[class*=-BGC176] { + background-color: #D787D7 +} + +div.highlight .-Color[class*=-C177] { + color: #D787FF +} + +div.highlight .-Color[class*=-BGC177] { + background-color: #D787FF +} + +div.highlight .-Color[class*=-C178] { + color: #D7AF00 +} + +div.highlight .-Color[class*=-BGC178] { + background-color: #D7AF00 +} + +div.highlight .-Color[class*=-C179] { + color: #D7AF5F +} + +div.highlight .-Color[class*=-BGC179] { + background-color: #D7AF5F +} + +div.highlight .-Color[class*=-C180] { + color: #D7AF87 +} + +div.highlight .-Color[class*=-BGC180] { + background-color: #D7AF87 +} + +div.highlight .-Color[class*=-C181] { + color: #D7AFAF +} + +div.highlight .-Color[class*=-BGC181] { + background-color: #D7AFAF +} + +div.highlight .-Color[class*=-C182] { + color: #D7AFD7 +} + +div.highlight .-Color[class*=-BGC182] { + background-color: #D7AFD7 +} + +div.highlight .-Color[class*=-C183] { + color: #D7AFFF +} + +div.highlight .-Color[class*=-BGC183] { + background-color: #D7AFFF +} + +div.highlight .-Color[class*=-C184] { + color: #D7D700 +} + +div.highlight .-Color[class*=-BGC184] { + background-color: #D7D700 +} + +div.highlight .-Color[class*=-C185] { + color: #D7D75F +} + +div.highlight .-Color[class*=-BGC185] { + background-color: #D7D75F +} + +div.highlight .-Color[class*=-C186] { + color: #D7D787 +} + +div.highlight .-Color[class*=-BGC186] { + background-color: #D7D787 +} + +div.highlight .-Color[class*=-C187] { + color: #D7D7AF +} + +div.highlight .-Color[class*=-BGC187] { + background-color: #D7D7AF +} + +div.highlight .-Color[class*=-C188] { + color: #D7D7D7 +} + +div.highlight .-Color[class*=-BGC188] { + background-color: #D7D7D7 +} + +div.highlight .-Color[class*=-C189] { + color: #D7D7FF +} + +div.highlight .-Color[class*=-BGC189] { + background-color: #D7D7FF +} + +div.highlight .-Color[class*=-C190] { + color: #D7FF00 +} + +div.highlight .-Color[class*=-BGC190] { + background-color: #D7FF00 +} + +div.highlight .-Color[class*=-C191] { + color: #D7FF5F +} + +div.highlight .-Color[class*=-BGC191] { + background-color: #D7FF5F +} + +div.highlight .-Color[class*=-C192] { + color: #D7FF87 +} + +div.highlight .-Color[class*=-BGC192] { + background-color: #D7FF87 +} + +div.highlight .-Color[class*=-C193] { + color: #D7FFAF +} + +div.highlight .-Color[class*=-BGC193] { + background-color: #D7FFAF +} + +div.highlight .-Color[class*=-C194] { + color: #D7FFD7 +} + +div.highlight .-Color[class*=-BGC194] { + background-color: #D7FFD7 +} + +div.highlight .-Color[class*=-C195] { + color: #D7FFFF +} + +div.highlight .-Color[class*=-BGC195] { + background-color: #D7FFFF +} + +div.highlight .-Color[class*=-C196] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC196] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C197] { + color: #FF005F +} + +div.highlight .-Color[class*=-BGC197] { + background-color: #FF005F +} + +div.highlight .-Color[class*=-C198] { + color: #FF0087 +} + +div.highlight .-Color[class*=-BGC198] { + background-color: #FF0087 +} + +div.highlight .-Color[class*=-C199] { + color: #FF00AF +} + +div.highlight .-Color[class*=-BGC199] { + background-color: #FF00AF +} + +div.highlight .-Color[class*=-C200] { + color: #FF00D7 +} + +div.highlight .-Color[class*=-BGC200] { + background-color: #FF00D7 +} + +div.highlight .-Color[class*=-C201] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC201] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C202] { + color: #FF5F00 +} + +div.highlight .-Color[class*=-BGC202] { + background-color: #FF5F00 +} + +div.highlight .-Color[class*=-C203] { + color: #FF5F5F +} + +div.highlight .-Color[class*=-BGC203] { + background-color: #FF5F5F +} + +div.highlight .-Color[class*=-C204] { + color: #FF5F87 +} + +div.highlight .-Color[class*=-BGC204] { + background-color: #FF5F87 +} + +div.highlight .-Color[class*=-C205] { + color: #FF5FAF +} + +div.highlight .-Color[class*=-BGC205] { + background-color: #FF5FAF +} + +div.highlight .-Color[class*=-C206] { + color: #FF5FD7 +} + +div.highlight .-Color[class*=-BGC206] { + background-color: #FF5FD7 +} + +div.highlight .-Color[class*=-C207] { + color: #FF5FFF +} + +div.highlight .-Color[class*=-BGC207] { + background-color: #FF5FFF +} + +div.highlight .-Color[class*=-C208] { + color: #FF8700 +} + +div.highlight .-Color[class*=-BGC208] { + background-color: #FF8700 +} + +div.highlight .-Color[class*=-C209] { + color: #FF875F +} + +div.highlight .-Color[class*=-BGC209] { + background-color: #FF875F +} + +div.highlight .-Color[class*=-C210] { + color: #FF8787 +} + +div.highlight .-Color[class*=-BGC210] { + background-color: #FF8787 +} + +div.highlight .-Color[class*=-C211] { + color: #FF87AF +} + +div.highlight .-Color[class*=-BGC211] { + background-color: #FF87AF +} + +div.highlight .-Color[class*=-C212] { + color: #FF87D7 +} + +div.highlight .-Color[class*=-BGC212] { + background-color: #FF87D7 +} + +div.highlight .-Color[class*=-C213] { + color: #FF87FF +} + +div.highlight .-Color[class*=-BGC213] { + background-color: #FF87FF +} + +div.highlight .-Color[class*=-C214] { + color: #FFAF00 +} + +div.highlight .-Color[class*=-BGC214] { + background-color: #FFAF00 +} + +div.highlight .-Color[class*=-C215] { + color: #FFAF5F +} + +div.highlight .-Color[class*=-BGC215] { + background-color: #FFAF5F +} + +div.highlight .-Color[class*=-C216] { + color: #FFAF87 +} + +div.highlight .-Color[class*=-BGC216] { + background-color: #FFAF87 +} + +div.highlight .-Color[class*=-C217] { + color: #FFAFAF +} + +div.highlight .-Color[class*=-BGC217] { + background-color: #FFAFAF +} + +div.highlight .-Color[class*=-C218] { + color: #FFAFD7 +} + +div.highlight .-Color[class*=-BGC218] { + background-color: #FFAFD7 +} + +div.highlight .-Color[class*=-C219] { + color: #FFAFFF +} + +div.highlight .-Color[class*=-BGC219] { + background-color: #FFAFFF +} + +div.highlight .-Color[class*=-C220] { + color: #FFD700 +} + +div.highlight .-Color[class*=-BGC220] { + background-color: #FFD700 +} + +div.highlight .-Color[class*=-C221] { + color: #FFD75F +} + +div.highlight .-Color[class*=-BGC221] { + background-color: #FFD75F +} + +div.highlight .-Color[class*=-C222] { + color: #FFD787 +} + +div.highlight .-Color[class*=-BGC222] { + background-color: #FFD787 +} + +div.highlight .-Color[class*=-C223] { + color: #FFD7AF +} + +div.highlight .-Color[class*=-BGC223] { + background-color: #FFD7AF +} + +div.highlight .-Color[class*=-C224] { + color: #FFD7D7 +} + +div.highlight .-Color[class*=-BGC224] { + background-color: #FFD7D7 +} + +div.highlight .-Color[class*=-C225] { + color: #FFD7FF +} + +div.highlight .-Color[class*=-BGC225] { + background-color: #FFD7FF +} + +div.highlight .-Color[class*=-C226] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC226] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C227] { + color: #FFFF5F +} + +div.highlight .-Color[class*=-BGC227] { + background-color: #FFFF5F +} + +div.highlight .-Color[class*=-C228] { + color: #FFFF87 +} + +div.highlight .-Color[class*=-BGC228] { + background-color: #FFFF87 +} + +div.highlight .-Color[class*=-C229] { + color: #FFFFAF +} + +div.highlight .-Color[class*=-BGC229] { + background-color: #FFFFAF +} + +div.highlight .-Color[class*=-C230] { + color: #FFFFD7 +} + +div.highlight .-Color[class*=-BGC230] { + background-color: #FFFFD7 +} + +div.highlight .-Color[class*=-C231] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC231] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C232] { + color: #080808 +} + +div.highlight .-Color[class*=-BGC232] { + background-color: #080808 +} + +div.highlight .-Color[class*=-C233] { + color: #121212 +} + +div.highlight .-Color[class*=-BGC233] { + background-color: #121212 +} + +div.highlight .-Color[class*=-C234] { + color: #1C1C1C +} + +div.highlight .-Color[class*=-BGC234] { + background-color: #1C1C1C +} + +div.highlight .-Color[class*=-C235] { + color: #262626 +} + +div.highlight .-Color[class*=-BGC235] { + background-color: #262626 +} + +div.highlight .-Color[class*=-C236] { + color: #303030 +} + +div.highlight .-Color[class*=-BGC236] { + background-color: #303030 +} + +div.highlight .-Color[class*=-C237] { + color: #3A3A3A +} + +div.highlight .-Color[class*=-BGC237] { + background-color: #3A3A3A +} + +div.highlight .-Color[class*=-C238] { + color: #444444 +} + +div.highlight .-Color[class*=-BGC238] { + background-color: #444444 +} + +div.highlight .-Color[class*=-C239] { + color: #4E4E4E +} + +div.highlight .-Color[class*=-BGC239] { + background-color: #4E4E4E +} + +div.highlight .-Color[class*=-C240] { + color: #585858 +} + +div.highlight .-Color[class*=-BGC240] { + background-color: #585858 +} + +div.highlight .-Color[class*=-C241] { + color: #626262 +} + +div.highlight .-Color[class*=-BGC241] { + background-color: #626262 +} + +div.highlight .-Color[class*=-C242] { + color: #6C6C6C +} + +div.highlight .-Color[class*=-BGC242] { + background-color: #6C6C6C +} + +div.highlight .-Color[class*=-C243] { + color: #767676 +} + +div.highlight .-Color[class*=-BGC243] { + background-color: #767676 +} + +div.highlight .-Color[class*=-C244] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC244] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C245] { + color: #8A8A8A +} + +div.highlight .-Color[class*=-BGC245] { + background-color: #8A8A8A +} + +div.highlight .-Color[class*=-C246] { + color: #949494 +} + +div.highlight .-Color[class*=-BGC246] { + background-color: #949494 +} + +div.highlight .-Color[class*=-C247] { + color: #9E9E9E +} + +div.highlight .-Color[class*=-BGC247] { + background-color: #9E9E9E +} + +div.highlight .-Color[class*=-C248] { + color: #A8A8A8 +} + +div.highlight .-Color[class*=-BGC248] { + background-color: #A8A8A8 +} + +div.highlight .-Color[class*=-C249] { + color: #B2B2B2 +} + +div.highlight .-Color[class*=-BGC249] { + background-color: #B2B2B2 +} + +div.highlight .-Color[class*=-C250] { + color: #BCBCBC +} + +div.highlight .-Color[class*=-BGC250] { + background-color: #BCBCBC +} + +div.highlight .-Color[class*=-C251] { + color: #C6C6C6 +} + +div.highlight .-Color[class*=-BGC251] { + background-color: #C6C6C6 +} + +div.highlight .-Color[class*=-C252] { + color: #D0D0D0 +} + +div.highlight .-Color[class*=-BGC252] { + background-color: #D0D0D0 +} + +div.highlight .-Color[class*=-C253] { + color: #DADADA +} + +div.highlight .-Color[class*=-BGC253] { + background-color: #DADADA +} + +div.highlight .-Color[class*=-C254] { + color: #E4E4E4 +} + +div.highlight .-Color[class*=-BGC254] { + background-color: #E4E4E4 +} + +div.highlight .-Color[class*=-C255] { + color: #EEEEEE +} + +div.highlight .-Color[class*=-BGC255] { + background-color: #EEEEEE +} diff --git a/branch/main/singlehtml/_static/plus.png b/branch/main/singlehtml/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/branch/main/singlehtml/_static/plus.png differ diff --git a/branch/main/singlehtml/_static/pygments.css b/branch/main/singlehtml/_static/pygments.css new file mode 100644 index 0000000..84ab303 --- /dev/null +++ b/branch/main/singlehtml/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/branch/main/singlehtml/_static/searchtools.js b/branch/main/singlehtml/_static/searchtools.js new file mode 100644 index 0000000..b08d58c --- /dev/null +++ b/branch/main/singlehtml/_static/searchtools.js @@ -0,0 +1,620 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + "Search finished, found ${resultCount} page(s) matching the search query." + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/branch/main/singlehtml/_static/sphinx_highlight.js b/branch/main/singlehtml/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/branch/main/singlehtml/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/branch/main/singlehtml/_static/sphinx_lesson.css b/branch/main/singlehtml/_static/sphinx_lesson.css new file mode 100644 index 0000000..14b20c6 --- /dev/null +++ b/branch/main/singlehtml/_static/sphinx_lesson.css @@ -0,0 +1,51 @@ +/* sphinx_lesson.css */ + +body.wy-body-for-nav img.with-border { + border: 2px solid; +} + +.rst-content .admonition-no-content { + padding-bottom: 0px; +} + +.rst-content .demo > .admonition-title::before { + content: "\01F440"; /* Eyes */ } +.rst-content .type-along > .admonition-title::before { + content: "\02328\0FE0F"; /* Keyboard */ } +.rst-content .exercise > .admonition-title::before { + content: "\0270D\0FE0F"; /* Hand */ } +.rst-content .solution > .admonition-title::before { + content: "\02714\0FE0E"; /* Check mark */ } +.rst-content .homework > .admonition-title::before { + content: "\01F4DD"; /* Memo */ } +.rst-content .discussion > .admonition-title::before { + content: "\01F4AC"; /* Speech balloon */ } +.rst-content .questions > .admonition-title::before { + content: "\02753\0FE0E"; /* Question mark */ } +.rst-content .prerequisites > .admonition-title::before { + content: "\02699"; /* Gear */ } +.rst-content .seealso > .admonition-title::before { + content: "\027A1\0FE0E"; /* Question mark */ } + + +/* instructor-note */ +.rst-content .instructor-note { + background: #e7e7e7; +} +.rst-content .instructor-note > .admonition-title { + background: #6a6a6a; +} +.rst-content .instructor-note > .admonition-title::before { + content: ""; +} + + +/* sphinx_toggle_button, make the font white */ +.rst-content .toggle.admonition button.toggle-button { + color: white; +} + +/* sphinx-togglebutton, remove underflow when toggled to hidden mode */ +.rst-content .admonition.toggle-hidden { + padding-bottom: 0px; +} diff --git a/branch/main/singlehtml/_static/sphinx_rtd_theme_ext_color_contrast.css b/branch/main/singlehtml/_static/sphinx_rtd_theme_ext_color_contrast.css new file mode 100644 index 0000000..e68feb8 --- /dev/null +++ b/branch/main/singlehtml/_static/sphinx_rtd_theme_ext_color_contrast.css @@ -0,0 +1,47 @@ +/* The following are for web accessibility of sphinx_rtd_theme: they + * solve some of the most frequent contrast issues. Remove when this + * solved: + * https://github.com/readthedocs/sphinx_rtd_theme/issues/971 + */ +/* background: #fcfcfc, note boxes #E7F2FA */ +a { color: #2573A7; } /* original #2980B9, #1F5C84; */ +body { color: #242424; } /* original #404040, #383838 */ +.wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a { + color: #ffffff; +} /* original #fcfcfc */ +footer { color: #737373; } /* original gray=#808080*/ +footer span.commit code, footer span.commit .rst-content tt, .rst-content footer span.commit tt { + color: #737373; +} /* original gray=#808080*/ +.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal { + color: #AB2314; +} +/* Sidebar background */ +.wy-side-nav-search { background-color: #277CB4;} + +/* Same, but for pygments */ +.highlight .ch { color: #3E7A89; } /* #! line */ +.highlight .c1 { color: #3E7A89; } /* also comments */ +.highlight .nv { color: #AD3ECC; } /* variable */ +.highlight .gp { color: #B45608; } /* prompt character, $*/ +.highlight .si { color: #3975B1; } /* ${} variable text */ +.highlight .nc { color: #0C78A7; } + +/* Sphinx admonitions */ +/* warning */ +.wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .rst-content .wy-alert-warning.admonition .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title, .rst-content .wy-alert-warning.admonition .admonition-title { + background: #B15E16; } +/* important */ +.wy-alert.wy-alert-success .wy-alert-title, .rst-content .wy-alert-success.note .wy-alert-title, .rst-content .wy-alert-success.attention .wy-alert-title, .rst-content .wy-alert-success.caution .wy-alert-title, .rst-content .wy-alert-success.danger .wy-alert-title, .rst-content .wy-alert-success.error .wy-alert-title, .rst-content .hint .wy-alert-title, .rst-content .important .wy-alert-title, .rst-content .tip .wy-alert-title, .rst-content .wy-alert-success.warning .wy-alert-title, .rst-content .wy-alert-success.seealso .wy-alert-title, .rst-content .wy-alert-success.admonition-todo .wy-alert-title, .rst-content .wy-alert-success.admonition .wy-alert-title, .wy-alert.wy-alert-success .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-success .admonition-title, .rst-content .wy-alert-success.note .admonition-title, .rst-content .wy-alert-success.attention .admonition-title, .rst-content .wy-alert-success.caution .admonition-title, .rst-content .wy-alert-success.danger .admonition-title, .rst-content .wy-alert-success.error .admonition-title, .rst-content .hint .admonition-title, .rst-content .important .admonition-title, .rst-content .tip .admonition-title, .rst-content .wy-alert-success.warning .admonition-title, .rst-content .wy-alert-success.seealso .admonition-title, .rst-content .wy-alert-success.admonition-todo .admonition-title, .rst-content .wy-alert-success.admonition .admonition-title { + background: #12826C; } +/* seealso, note, etc */ +.wy-alert.wy-alert-info .wy-alert-title, .rst-content .note .wy-alert-title, .rst-content .wy-alert-info.attention .wy-alert-title, .rst-content .wy-alert-info.caution .wy-alert-title, .rst-content .wy-alert-info.danger .wy-alert-title, .rst-content .wy-alert-info.error .wy-alert-title, .rst-content .wy-alert-info.hint .wy-alert-title, .rst-content .wy-alert-info.important .wy-alert-title, .rst-content .wy-alert-info.tip .wy-alert-title, .rst-content .wy-alert-info.warning .wy-alert-title, .rst-content .seealso .wy-alert-title, .rst-content .wy-alert-info.admonition-todo .wy-alert-title, .rst-content .wy-alert-info.admonition .wy-alert-title, .wy-alert.wy-alert-info .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-info .admonition-title, .rst-content .note .admonition-title, .rst-content .wy-alert-info.attention .admonition-title, .rst-content .wy-alert-info.caution .admonition-title, .rst-content .wy-alert-info.danger .admonition-title, .rst-content .wy-alert-info.error .admonition-title, .rst-content .wy-alert-info.hint .admonition-title, .rst-content .wy-alert-info.important .admonition-title, .rst-content .wy-alert-info.tip .admonition-title, .rst-content .wy-alert-info.warning .admonition-title, .rst-content .seealso .admonition-title, .rst-content .wy-alert-info.admonition-todo .admonition-title, .rst-content .wy-alert-info.admonition .admonition-title { + background: #277CB4; } +/* error, danger */ +.rst-content .danger .admonition-title, .rst-content .danger .wy-alert-title, .rst-content .error .admonition-title, .rst-content .error .wy-alert-title, .rst-content .wy-alert-danger.admonition-todo .admonition-title, .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, .rst-content .wy-alert-danger.admonition .admonition-title, .rst-content .wy-alert-danger.admonition .wy-alert-title, .rst-content .wy-alert-danger.attention .admonition-title, .rst-content .wy-alert-danger.attention .wy-alert-title, .rst-content .wy-alert-danger.caution .admonition-title, .rst-content .wy-alert-danger.caution .wy-alert-title, .rst-content .wy-alert-danger.hint .admonition-title, .rst-content .wy-alert-danger.hint .wy-alert-title, .rst-content .wy-alert-danger.important .admonition-title, .rst-content .wy-alert-danger.important .wy-alert-title, .rst-content .wy-alert-danger.note .admonition-title, .rst-content .wy-alert-danger.note .wy-alert-title, .rst-content .wy-alert-danger.seealso .admonition-title, .rst-content .wy-alert-danger.seealso .wy-alert-title, .rst-content .wy-alert-danger.tip .admonition-title, .rst-content .wy-alert-danger.tip .wy-alert-title, .rst-content .wy-alert-danger.warning .admonition-title, .rst-content .wy-alert-danger.warning .wy-alert-title, .rst-content .wy-alert.wy-alert-danger .admonition-title, .wy-alert.wy-alert-danger .rst-content .admonition-title, .wy-alert.wy-alert-danger .wy-alert-title { + background: #e31704; +} + +/* Generic admonition titles */ +.wy-alert-title, .rst-content .admonition-title { + background: #277CB4; } diff --git a/branch/main/singlehtml/_static/style.css b/branch/main/singlehtml/_static/style.css new file mode 100644 index 0000000..59e60ee --- /dev/null +++ b/branch/main/singlehtml/_static/style.css @@ -0,0 +1,6 @@ +.rst-content .objectives { + background: #fee0d2; +} +.rst-content .objectives > .admonition-title { + background: #fc9272; +} diff --git a/branch/main/singlehtml/_static/tabs.css b/branch/main/singlehtml/_static/tabs.css new file mode 100644 index 0000000..957ba60 --- /dev/null +++ b/branch/main/singlehtml/_static/tabs.css @@ -0,0 +1,89 @@ +.sphinx-tabs { + margin-bottom: 1rem; +} + +[role="tablist"] { + border-bottom: 1px solid #a0b3bf; +} + +.sphinx-tabs-tab { + position: relative; + font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif; + color: #1D5C87; + line-height: 24px; + margin: 0; + font-size: 16px; + font-weight: 400; + background-color: rgba(255, 255, 255, 0); + border-radius: 5px 5px 0 0; + border: 0; + padding: 1rem 1.5rem; + margin-bottom: 0; +} + +.sphinx-tabs-tab[aria-selected="true"] { + font-weight: 700; + border: 1px solid #a0b3bf; + border-bottom: 1px solid white; + margin: -1px; + background-color: white; +} + +.sphinx-tabs-tab:focus { + z-index: 1; + outline-offset: 1px; +} + +.sphinx-tabs-panel { + position: relative; + padding: 1rem; + border: 1px solid #a0b3bf; + margin: 0px -1px -1px -1px; + border-radius: 0 0 5px 5px; + border-top: 0; + background: white; +} + +.sphinx-tabs-panel.code-tab { + padding: 0.4rem; +} + +.sphinx-tab img { + margin-bottom: 24 px; +} + +/* Dark theme preference styling */ + +@media (prefers-color-scheme: dark) { + body[data-theme="auto"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); + } + + body[data-theme="auto"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); + } + + body[data-theme="auto"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 1px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); + } +} + +/* Explicit dark theme styling */ + +body[data-theme="dark"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); +} + +body[data-theme="dark"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); +} + +body[data-theme="dark"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 2px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); +} diff --git a/branch/main/singlehtml/_static/tabs.js b/branch/main/singlehtml/_static/tabs.js new file mode 100644 index 0000000..48dc303 --- /dev/null +++ b/branch/main/singlehtml/_static/tabs.js @@ -0,0 +1,145 @@ +try { + var session = window.sessionStorage || {}; +} catch (e) { + var session = {}; +} + +window.addEventListener("DOMContentLoaded", () => { + const allTabs = document.querySelectorAll('.sphinx-tabs-tab'); + const tabLists = document.querySelectorAll('[role="tablist"]'); + + allTabs.forEach(tab => { + tab.addEventListener("click", changeTabs); + }); + + tabLists.forEach(tabList => { + tabList.addEventListener("keydown", keyTabs); + }); + + // Restore group tab selection from session + const lastSelected = session.getItem('sphinx-tabs-last-selected'); + if (lastSelected != null) selectNamedTabs(lastSelected); +}); + +/** + * Key focus left and right between sibling elements using arrows + * @param {Node} e the element in focus when key was pressed + */ +function keyTabs(e) { + const tab = e.target; + let nextTab = null; + if (e.keyCode === 39 || e.keyCode === 37) { + tab.setAttribute("tabindex", -1); + // Move right + if (e.keyCode === 39) { + nextTab = tab.nextElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.firstElementChild; + } + // Move left + } else if (e.keyCode === 37) { + nextTab = tab.previousElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.lastElementChild; + } + } + } + + if (nextTab !== null) { + nextTab.setAttribute("tabindex", 0); + nextTab.focus(); + } +} + +/** + * Select or deselect clicked tab. If a group tab + * is selected, also select tab in other tabLists. + * @param {Node} e the element that was clicked + */ +function changeTabs(e) { + // Use this instead of the element that was clicked, in case it's a child + const notSelected = this.getAttribute("aria-selected") === "false"; + const positionBefore = this.parentNode.getBoundingClientRect().top; + const notClosable = !this.parentNode.classList.contains("closeable"); + + deselectTabList(this); + + if (notSelected || notClosable) { + selectTab(this); + const name = this.getAttribute("name"); + selectNamedTabs(name, this.id); + + if (this.classList.contains("group-tab")) { + // Persist during session + session.setItem('sphinx-tabs-last-selected', name); + } + } + + const positionAfter = this.parentNode.getBoundingClientRect().top; + const positionDelta = positionAfter - positionBefore; + // Scroll to offset content resizing + window.scrollTo(0, window.scrollY + positionDelta); +} + +/** + * Select tab and show associated panel. + * @param {Node} tab tab to select + */ +function selectTab(tab) { + tab.setAttribute("aria-selected", true); + + // Show the associated panel + document + .getElementById(tab.getAttribute("aria-controls")) + .removeAttribute("hidden"); +} + +/** + * Hide the panels associated with all tabs within the + * tablist containing this tab. + * @param {Node} tab a tab within the tablist to deselect + */ +function deselectTabList(tab) { + const parent = tab.parentNode; + const grandparent = parent.parentNode; + + Array.from(parent.children) + .forEach(t => t.setAttribute("aria-selected", false)); + + Array.from(grandparent.children) + .slice(1) // Skip tablist + .forEach(panel => panel.setAttribute("hidden", true)); +} + +/** + * Select grouped tabs with the same name, but no the tab + * with the given id. + * @param {Node} name name of grouped tab to be selected + * @param {Node} clickedId id of clicked tab + */ +function selectNamedTabs(name, clickedId=null) { + const groupedTabs = document.querySelectorAll(`.sphinx-tabs-tab[name="${name}"]`); + const tabLists = Array.from(groupedTabs).map(tab => tab.parentNode); + + tabLists + .forEach(tabList => { + // Don't want to change the tabList containing the clicked tab + const clickedTab = tabList.querySelector(`[id="${clickedId}"]`); + if (clickedTab === null ) { + // Select first tab with matching name + const tab = tabList.querySelector(`.sphinx-tabs-tab[name="${name}"]`); + deselectTabList(tab); + selectTab(tab); + } + }) +} + +if (typeof exports === 'undefined') { + exports = {}; +} + +exports.keyTabs = keyTabs; +exports.changeTabs = changeTabs; +exports.selectTab = selectTab; +exports.deselectTabList = deselectTabList; +exports.selectNamedTabs = selectNamedTabs; diff --git a/branch/main/singlehtml/_static/term_role_formatting.css b/branch/main/singlehtml/_static/term_role_formatting.css new file mode 100644 index 0000000..0b66095 --- /dev/null +++ b/branch/main/singlehtml/_static/term_role_formatting.css @@ -0,0 +1,4 @@ +/* Make terms bold */ +a.reference span.std-term { + font-weight: bold; +} diff --git a/branch/main/singlehtml/_static/togglebutton.css b/branch/main/singlehtml/_static/togglebutton.css new file mode 100644 index 0000000..54a6787 --- /dev/null +++ b/branch/main/singlehtml/_static/togglebutton.css @@ -0,0 +1,160 @@ +/** + * Admonition-based toggles + */ + +/* Visibility of the target */ +.admonition.toggle .admonition-title ~ * { + transition: opacity .3s, height .3s; +} + +/* Toggle buttons inside admonitions so we see the title */ +.admonition.toggle { + position: relative; +} + +/* Titles should cut off earlier to avoid overlapping w/ button */ +.admonition.toggle .admonition-title { + padding-right: 25%; + cursor: pointer; +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:hover { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 1%); +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:active { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 3%); +} + +/* Remove extra whitespace below the admonition title when hidden */ +.admonition.toggle-hidden { + padding-bottom: 0; +} + +.admonition.toggle-hidden .admonition-title { + margin-bottom: 0; +} + +/* hides all the content of a page until de-toggled */ +.admonition.toggle-hidden .admonition-title ~ * { + height: 0; + margin: 0; + opacity: 0; + visibility: hidden; +} + +/* General button style and position*/ +button.toggle-button { + /** + * Background and shape. By default there's no background + * but users can style as they wish + */ + background: none; + border: none; + outline: none; + + /* Positioning just inside the admonition title */ + position: absolute; + right: 0.5em; + padding: 0px; + border: none; + outline: none; +} + +/* Display the toggle hint on wide screens */ +@media (min-width: 768px) { + button.toggle-button.toggle-button-hidden:before { + content: attr(data-toggle-hint); /* This will be filled in by JS */ + font-size: .8em; + align-self: center; + } +} + +/* Icon behavior */ +.tb-icon { + transition: transform .2s ease-out; + height: 1.5em; + width: 1.5em; + stroke: currentColor; /* So that we inherit the color of other text */ +} + +/* The icon should point right when closed, down when open. */ +/* Open */ +.admonition.toggle button .tb-icon { + transform: rotate(90deg); +} + +/* Closed */ +.admonition.toggle button.toggle-button-hidden .tb-icon { + transform: rotate(0deg); +} + +/* With details toggles, we don't rotate the icon so it points right */ +details.toggle-details .tb-icon { + height: 1.4em; + width: 1.4em; + margin-top: 0.1em; /* To center the button vertically */ +} + + +/** + * Details-based toggles. + * In this case, we wrap elements with `.toggle` in a details block. + */ + +/* Details blocks */ +details.toggle-details { + margin: 1em 0; +} + + +details.toggle-details summary { + display: flex; + align-items: center; + cursor: pointer; + list-style: none; + border-radius: .2em; + border-left: 3px solid #1976d2; + background-color: rgb(204 204 204 / 10%); + padding: 0.2em 0.7em 0.3em 0.5em; /* Less padding on left because the SVG has left margin */ + font-size: 0.9em; +} + +details.toggle-details summary:hover { + background-color: rgb(204 204 204 / 20%); +} + +details.toggle-details summary:active { + background: rgb(204 204 204 / 28%); +} + +.toggle-details__summary-text { + margin-left: 0.2em; +} + +details.toggle-details[open] summary { + margin-bottom: .5em; +} + +details.toggle-details[open] summary .tb-icon { + transform: rotate(90deg); +} + +details.toggle-details[open] summary ~ * { + animation: toggle-fade-in .3s ease-out; +} + +@keyframes toggle-fade-in { + from {opacity: 0%;} + to {opacity: 100%;} +} + +/* Print rules - we hide all toggle button elements at print */ +@media print { + /* Always hide the summary so the button doesn't show up */ + details.toggle-details summary { + display: none; + } +} \ No newline at end of file diff --git a/branch/main/singlehtml/_static/togglebutton.js b/branch/main/singlehtml/_static/togglebutton.js new file mode 100644 index 0000000..215a7ee --- /dev/null +++ b/branch/main/singlehtml/_static/togglebutton.js @@ -0,0 +1,187 @@ +/** + * Add Toggle Buttons to elements + */ + +let toggleChevron = ` + + + +`; + +var initToggleItems = () => { + var itemsToToggle = document.querySelectorAll(togglebuttonSelector); + console.log(`[togglebutton]: Adding toggle buttons to ${itemsToToggle.length} items`) + // Add the button to each admonition and hook up a callback to toggle visibility + itemsToToggle.forEach((item, index) => { + if (item.classList.contains("admonition")) { + // If it's an admonition block, then we'll add a button inside + // Generate unique IDs for this item + var toggleID = `toggle-${index}`; + var buttonID = `button-${toggleID}`; + + item.setAttribute('id', toggleID); + if (!item.classList.contains("toggle")){ + item.classList.add("toggle"); + } + // This is the button that will be added to each item to trigger the toggle + var collapseButton = ` + `; + + title = item.querySelector(".admonition-title") + title.insertAdjacentHTML("beforeend", collapseButton); + thisButton = document.getElementById(buttonID); + + // Add click handlers for the button + admonition title (if admonition) + admonitionTitle = document.querySelector(`#${toggleID} > .admonition-title`) + if (admonitionTitle) { + // If an admonition, then make the whole title block clickable + admonitionTitle.addEventListener('click', toggleClickHandler); + admonitionTitle.dataset.target = toggleID + admonitionTitle.dataset.button = buttonID + } else { + // If not an admonition then we'll listen for the button click + thisButton.addEventListener('click', toggleClickHandler); + } + + // Now hide the item for this toggle button unless explicitly noted to show + if (!item.classList.contains("toggle-shown")) { + toggleHidden(thisButton); + } + } else { + // If not an admonition, wrap the block in a
block + // Define the structure of the details block and insert it as a sibling + var detailsBlock = ` +
+ + ${toggleChevron} + ${toggleHintShow} + +
`; + item.insertAdjacentHTML("beforebegin", detailsBlock); + + // Now move the toggle-able content inside of the details block + details = item.previousElementSibling + details.appendChild(item) + item.classList.add("toggle-details__container") + + // Set up a click trigger to change the text as needed + details.addEventListener('click', (click) => { + let parent = click.target.parentElement; + if (parent.tagName.toLowerCase() == "details") { + summary = parent.querySelector("summary"); + details = parent; + } else { + summary = parent; + details = parent.parentElement; + } + // Update the inner text for the proper hint + if (details.open) { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintShow; + } else { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintHide; + } + + }); + + // If we have a toggle-shown class, open details block should be open + if (item.classList.contains("toggle-shown")) { + details.click(); + } + } + }) +}; + +// This should simply add / remove the collapsed class and change the button text +var toggleHidden = (button) => { + target = button.dataset['target'] + var itemToToggle = document.getElementById(target); + if (itemToToggle.classList.contains("toggle-hidden")) { + itemToToggle.classList.remove("toggle-hidden"); + button.classList.remove("toggle-button-hidden"); + } else { + itemToToggle.classList.add("toggle-hidden"); + button.classList.add("toggle-button-hidden"); + } +} + +var toggleClickHandler = (click) => { + // Be cause the admonition title is clickable and extends to the whole admonition + // We only look for a click event on this title to trigger the toggle. + + if (click.target.classList.contains("admonition-title")) { + button = click.target.querySelector(".toggle-button"); + } else if (click.target.classList.contains("tb-icon")) { + // We've clicked the icon and need to search up one parent for the button + button = click.target.parentElement; + } else if (click.target.tagName == "polyline") { + // We've clicked the SVG elements inside the button, need to up 2 layers + button = click.target.parentElement.parentElement; + } else if (click.target.classList.contains("toggle-button")) { + // We've clicked the button itself and so don't need to do anything + button = click.target; + } else { + console.log(`[togglebutton]: Couldn't find button for ${click.target}`) + } + target = document.getElementById(button.dataset['button']); + toggleHidden(target); +} + +// If we want to blanket-add toggle classes to certain cells +var addToggleToSelector = () => { + const selector = ""; + if (selector.length > 0) { + document.querySelectorAll(selector).forEach((item) => { + item.classList.add("toggle"); + }) + } +} + +// Helper function to run when the DOM is finished +const sphinxToggleRunWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} +sphinxToggleRunWhenDOMLoaded(addToggleToSelector) +sphinxToggleRunWhenDOMLoaded(initToggleItems) + +/** Toggle details blocks to be open when printing */ +if (toggleOpenOnPrint == "true") { + window.addEventListener("beforeprint", () => { + // Open the details + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.dataset["togglestatus"] = el.open; + el.open = true; + }); + + // Open the admonitions + document.querySelectorAll(".admonition.toggle.toggle-hidden").forEach((el) => { + console.log(el); + el.querySelector("button.toggle-button").click(); + el.dataset["toggle_after_print"] = "true"; + }); + }); + window.addEventListener("afterprint", () => { + // Re-close the details that were closed + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.open = el.dataset["togglestatus"] == "true"; + delete el.dataset["togglestatus"]; + }); + + // Re-close the admonition toggle buttons + document.querySelectorAll(".admonition.toggle").forEach((el) => { + if (el.dataset["toggle_after_print"] == "true") { + el.querySelector("button.toggle-button").click(); + delete el.dataset["toggle_after_print"]; + } + }); + }); +} diff --git a/branch/main/singlehtml/index.html b/branch/main/singlehtml/index.html new file mode 100644 index 0000000..8dac0a1 --- /dev/null +++ b/branch/main/singlehtml/index.html @@ -0,0 +1,3103 @@ + + + + + + + Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Reproducible research software development using Python

+
+

Big-picture goal

+

This is a hands-on course on research software engineering. In this +workshop we assume that most workshop participants use Python in their work or +a leading a group which uses Python. Therefore, some of the examples will use +Python as the example language.

+

We will work with an example project and go through all the steps of a typical +software project. Once we have seen the building blocks, we will try to apply +them to own projects. Workshop participants will receive and also learn to give +constructive code feedback.

+
+
+

Prerequisites

+
+

Preparation

+
    +
  1. Get a GitHub account following these instructions.

  2. +
  3. You will need a text editor. If you don’t have a favorite one, we recommend +VS Code.

  4. +
  5. If you prefer to work in the terminal and not in VS Code, set up these two (skip this if you use VS Code):

    + +
  6. +
  7. One of these two software environments (if you are not sure which one to +choose or have no preference, choose Conda):

    + +
  8. +
  9. Optional and only on Linux: Apptainer following +these instructions.

  10. +
+
+
+
+

Schedule

+
+

Day 1

+ +
+
+

Day 2

+ +
+
+

Day 3

+ +
+
+

If we have time left

+ +
+
+

Conda environment

+

A Conda environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a environment.yml file that documents the dependencies:

+
name: coderefinery
+channels:
+  - conda-forge
+  - bioconda
+dependencies:
+  - python >= 3.10
+  - black
+  - click
+  - flit
+  - ipywidgets
+  - isort
+  - jupyterlab
+  - jupyterlab_code_formatter
+  - jupyterlab-git
+  - matplotlib
+  - myst-parser
+  - nbdime
+  - numpy
+  - pandas
+  - pytest
+  - pytest-cov
+  - scalene
+  - seaborn
+  - snakemake-minimal
+  - sphinx
+  - sphinx-autoapi
+  - sphinx-autobuild
+  - sphinx_rtd_theme >= 2.0
+  - vulture
+  - scikit-image
+
+
+
+
Before you create a virtual environment
+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create an environment.yml file and copy-paste the dependencies above into it.

  4. +
+
+
+
Choose the tool to manage the environment
+

If you are already using one of these tools, please continue using the tool that you like and know. +If you are new to this, we recommend using Miniconda or Miniforge.

+
    +
  • Anaconda

    +
      +
    • Advantages: easy to install, easy to use, good for beginners

    • +
    • Disadvantages: large download, installs more than we will need, license restrictions

    • +
    +
  • +
  • Miniconda

    +
      +
    • Advantages: small size, installs only what you need

    • +
    • Disadvantages: no graphical interface, license restrictions

    • +
    +
  • +
  • Miniforge

    +
      +
    • Advantages: small size, no license restrictions

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Micromamba

    +
      +
    • Advantages: fast, small size

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Pixi

    +
      +
    • Advantages: fast and new

    • +
    • Disadvantages: new and less tested and not documented here

    • +
    +
  • +
+
+
+
Creating the virtual environment
+
    +
  1. Open your terminal shell (e.g. Bash or Zsh).

  2. +
  3. Activate conda using conda activate or source ~/miniconda3/bin/activate.

  4. +
  5. Run the following command:

    +
    $ conda env create --file environment.yml
    +
    +
    +
  6. +
  7. Make sure that you see “coderefinery” in the output when you ask for a list of all available environments:

    +
    $ conda env list
    +
    +
    +
  8. +
+
+
+
+

Virtual environment

+

A virtual environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a requirements.txt file that documents the dependencies:

+
black
+click
+flit
+ipywidgets
+isort
+jupyterlab
+jupyterlab-code-formatter
+jupyterlab-git
+matplotlib
+myst-parser
+nbdime
+numpy
+pandas
+pytest
+pytest-cov
+scalene
+seaborn
+sphinx
+sphinx-autoapi
+sphinx-autobuild
+sphinx_rtd_theme >= 2.0
+vulture
+scikit-image
+
+
+
+
Before you create a virtual environment
+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create a requirements.txt file and copy-paste the dependencies above into it.

  4. +
+
+
+
Creating the virtual environment
+

Now create a virtual environment in this directory either using pip and +venv +(more traditional and safer) or using uv (more modern but also less tested):

+
+

Create a new virtual environment and activate it:

+
$ python3 -m venv coderefinery-environment
+$ source coderefinery-environment/bin/activate
+
+
+

Install the dependencies into the environment:

+
$ python3 -m pip install -r requirements.txt
+
+
+
+
+
+
+
+
+

Example project: Simulating the motion of planets

+

The example code that we will study +is a hopefully simple N-body simulation written in Python. It is not important +or expected that we understand the code in any detail.

+

The big picture is that the code simulates the motion of a number of +planets:

+
    +
  • We can choose the number of planets.

  • +
  • Each planet starts with a random position, velocity, and mass.

  • +
  • At each time step, the code calculates the gravitational force between each +pair of planets.

  • +
  • The forces accelerate each planet, the acceleration modifies the velocity, +the velocity modifies the position of each planet.

  • +
  • We can choose the number of time steps.

  • +
  • The units were chosen to make numbers easy to read.

  • +
+
+
Example run
+
+

Instructor note

+

The instructor demonstrates running the code on their computer.

+
+

The code is written to accept command-line arguments to specify the number +of planets and the number of time steps.

+

We first generate starting data:

+
$ python generate-data.py --num-planets 10 --output-file initial.csv
+
+
+

The generated file (initial.csv) could look like this:

+
px,py,pz,vx,vy,vz,mass
+-46.88,-42.51,88.33,-0.86,-0.18,0.55,6.70
+-5.29,17.09,-96.13,0.66,0.45,-0.17,3.51
+83.53,-92.83,-68.77,-0.26,-0.48,0.24,6.84
+-36.31,25.48,64.16,0.85,0.75,-0.56,1.53
+-68.38,-17.21,-97.07,0.60,0.26,0.69,6.63
+-48.37,-48.74,3.92,-0.92,-0.33,-0.93,8.60
+40.53,-75.50,44.18,-0.62,-0.31,-0.53,8.04
+-27.21,10.78,-78.82,-0.09,-0.55,-0.03,5.35
+88.42,-74.95,-45.85,0.81,0.68,0.56,5.36
+39.09,53.12,-59.54,-0.54,0.56,0.07,8.98
+
+
+

Then we can simulate their motion (in this case for 20 steps):

+
$ python simulate.py --num-steps 20 \
+                     --input-file initial.csv \
+                     --output-file final.csv
+
+
+

The --output-file (final.csv) is again a CSV file (comma-separated values) +and contains the final positions of all planets.

+

It is possible to run on multiple cores and to animate the result. +Here is an example with 100 planets:

+
$ python generate-data.py --num-planets 100 --output-file initial.csv
+
+$ python simulate.py --num-steps 50 \
+                     --input-file initial.csv \
+                     --output-file final.csv \
+                     --trajectories-file trajectories.npz \
+                     --num-cores 8
+
+$ python animate.py --initial-file initial.csv \
+                    --trajectories-file trajectories.npz \
+                    --output-file animation.mp4
+
+
+
+

Learning goals

+
    +
  • What are the most important steps to make this code reusable by others +and our future selves?

  • +
  • Be able to apply these techniques to your own code/script.

  • +
+
+
+

We will not focus on …

+
    +
  • … how the code works internally in detail.

  • +
  • … whether this is the most efficient algorithm.

  • +
  • … whether the code is numerically stable.

  • +
  • … how to code scales with the number of cores.

  • +
  • … whether it is portable to other operating systems (we will discuss this later).

  • +
+
+
+
+
+

Introduction to version control with Git and GitHub

+
+
+
Motivation
+
+

Objectives

+
    +
  • Browse commits and branches of a Git repository.

  • +
  • Remember that commits are like snapshots of the repository at a certain +point in time.

  • +
  • Know the difference between Git (something that tracks changes) and +GitHub/GitLab (a platform to host Git repositories).

  • +
+
+
+
Why do we need to keep track of versions?
+

Version control is an answer to the following questions (do you recognize some +of them?):

+
    +
  • “It broke … hopefully I have a working version somewhere?”

  • +
  • “Can you please send me the latest version?”

  • +
  • “Where is the latest version?”

  • +
  • “Which version are you using?”

  • +
  • “Which version have the authors used in the paper I am trying to reproduce?”

  • +
  • “Found a bug! Since when was it there?”

  • +
  • “I am sure it used to work. When did it change?”

  • +
  • “My laptop is gone. Is my thesis now gone?”

  • +
+
+
+
Demonstration
+
    +
  • Example repository: https://github.com/workshop-material/planets

  • +
  • Commits are like snapshots and if we break something we can go back to a +previous snapshot.

  • +
  • Commits carry metadata about changes: author, date, commit message, and +a checksum.

  • +
  • Branches are like parallel universes where you can experiment with +changes without affecting the default branch: +https://github.com/workshop-material/planets/network +(“Insights” -> “Network”)

  • +
  • With version control we can annotate code +(example).

  • +
  • Collaboration: We can fork (make a copy on GitHub), clone (make a copy +to our computer), review, compare, share, and discuss.

  • +
  • Code review: Others can suggest changes using pull requests or merge +requests. These can be reviewed and discussed before they are merged. +Conceptually, they are similar to “suggesting changes” in Google Docs.

  • +
+
+
+
Features: roll-back, branching, merging, collaboration
+
    +
  • Roll-back: you can always go back to a previous version and compare

  • +
  • Branching and merging:

    +
      +
    • Work on different ideas at the same time

    • +
    • Different people can work on the same code/project without interfering

    • +
    • You can experiment with an idea and discard it if it turns out to be a bad idea

    • +
    +
  • +
+
+Branching explained with a gopher +
+

Image created using https://gopherize.me/ +(inspiration).

+
+
+ +
+
+
Talking about code
+

Which of these two is more practical?

+
    +
  1. “Clone the code, go to the file ‘simulate.py’, and search for ‘force_between_planets’. +Oh! But make sure you use the version from September 2024.”

  2. +
  3. Or I can send you a permalink: https://github.com/workshop-material/planets/blob/1343ac0/simulate.py#L31C5-L39

  4. +
+
+
+
What we typically like to snapshot
+
    +
  • Software (this is how it started but Git/GitHub can track a lot more)

  • +
  • Scripts

  • +
  • Documents (plain text files much better suitable than Word documents)

  • +
  • Manuscripts (Git is great for collaborating/sharing LaTeX or Quarto manuscripts)

  • +
  • Configuration files

  • +
  • Website sources

  • +
  • Data

  • +
+
+

Discussion

+

In this example somebody tried to keep track of versions without a version +control system tool like Git. Discuss the following directory listing. What +possible problems do you anticipate with this kind of “version control”:

+
myproject-2019.zip
+myproject-2020-february.zip
+myproject-2021-august.zip
+myproject-2023-09-19-working.zip
+myproject-2023-09-21.zip
+myproject-2023-09-21-test.zip
+myproject-2023-09-21-myversion.zip
+myproject-2023-09-21-newfeature.zip
+...
+(100 more files like these)
+
+
+ +
+
+
+
+
Forking, cloning, and browsing
+

In this episode, we will look at an existing repository to understand how +all the pieces work together. Along the way, we will make a copy (by +forking and/or cloning) of the repository for us, which will be used +for our own changes.

+
+

Objectives

+
    +
  • See a real Git repository and understand what is inside of it.

  • +
  • Understand how version control allows advanced inspection of a +repository.

  • +
  • See how Git allows multiple people to work on the same project at the same time.

  • +
  • See the big picture instead of remembering a bunch of commands.

  • +
+
+
+
GitHub, VS Code, or command line
+

We offer three different paths for this exercise:

+
    +
  • GitHub (this is the one we will demonstrate)

  • +
  • VS Code (if you prefer to follow along using an editor)

  • +
  • Command line (for people comfortable with the command line)

  • +
+
+
+
Creating a copy of the repository by “forking” or “cloning”
+

A repository is a collection of files in one directory tracked by Git. A +GitHub repository is GitHub’s copy, which adds things like access control, +issue tracking, and discussions. Each GitHub repository is owned by a user or +organization, who controls access.

+

First, we need to make our own copy of the exercise repository. This will +become important later, when we make our own changes.

+
+Illustration of forking a repository on GitHub +
+

Illustration of forking a repository on GitHub.

+
+
+
+Illustration of cloning a repository to your computer +
+

Illustration of cloning a repository to your computer.

+
+
+
+Illustration of cloning a forked repository to your computer +
+

It is also possible to do this: to clone a forked repository to your computer.

+
+
+

At all times you should be aware of if you are looking at your repository +or the upstream repository (original repository):

+
    +
  • Your repository: https://github.com/USER/planets

  • +
  • Upstream repository: https://github.com/workshop-material/planets

  • +
+
+

How to create a fork

+
    +
  1. Go to the repository view on GitHub: https://github.com/workshop-material/planets

  2. +
  3. First, on GitHub, click the button that says “Fork”. It is towards +the top-right of the screen.

  4. +
  5. You should shortly be redirected to your copy of the repository +USER/planets.

  6. +
+
+
+

Instructor note

+

Before starting the exercise session show +how to fork the repository to own account +(above).

+
+
+
+
Exercise: Copy and browse an existing project
+

Work on this by yourself or in pairs.

+
+

Exercise preparation

+
+

In this case you will work on a fork.

+

You only need to open your own view, as described above. The browser +URL should look like https://github.com/USER/planets, where +USER is your GitHub username.

+
+
+
+

Exercise: Browsing an existing project (20 min)

+

Browse the example project and +explore commits and branches, either on a fork or on a clone. Take notes and +prepare questions. The hints are for the GitHub path in the browser.

+
    +
  1. Browse the commit history: Are commit messages understandable? +(Hint: “Commit history”, the timeline symbol, above the file list)

  2. +
  3. Compare the commit history with the network graph (“Insights” -> “Network”). Can you find the branches?

  4. +
  5. Try to find the history of commits for a single file, e.g. simulate.py. +(Hint: “History” button in the file view)

  6. +
  7. Which files include the word “position”? +(Hint: the GitHub search on top of the repository view)

  8. +
  9. In the simulate.py file, find out who modified each line last and when +(click on file, then click “Blame” button). Find out who modified the gravitational constant +last and in which commit. +(Hint: “Blame” view in the file view)

  10. +
  11. Can you use this code yourself? Are you allowed to share +modifications? +(Hint: look for a license file)

  12. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+
Solution and walk-through
+
+
(1) Basic browsing
+

The most basic thing to look at is the history of commits.

+
    +
  • This is visible from a button in the repository view. We see every +change, when, and who has committed.

  • +
  • Every change has a unique identifier, such as 244c993. This can +be used to identify both this change, and the whole project’s +version as of that change.

  • +
  • Clicking on a change in the view shows more.

  • +
+
+

Click on the timeline symbol in the repository view:

+
+Screenshot on GitHub of where to find the commit history +
+
+
+
+
(2) Compare commit history with network graph
+

The commit history we saw above looks linear: one commit after +another. But if we look at the network view, we see some branches and +merges. We’ll see how to do these later. This is another one of the +basic Git views.

+
+

In a new browser tab, open the “Insights” tab, and click on “Network”. +You can hover over the commit dots to see the person who committed and +how they correspond with the commits in the other view:

+
+Screenshot on GitHub of the network graph +
+
+
+
+
(3) How can you browse the history of a single file?
+

We see the history for the whole repository, but we can also see it +for a single file.

+
+

Navigate to the file view: Main page → simulate.py. +Click the “History” button near the top right.

+
+
+
+
(4) Which files include the word “position”?
+

Version control makes it very easy to find all occurrences of a single +word. This is useful for things like finding where functions or +variables are defined or used.

+
+

We go to the main file view. We click the Search magnifying +class at the very top, type “position”, and click enter. We see every +instance, including the context.

+
+

Searching in a forked repository will not work instantaneously!

+

It usually takes a few minutes before one can search for keywords in a forked repository +since it first needs to build the search index the very first time we search. +Start it, continue with other steps, then come back to this.

+
+
+
+
+
(5) Who modified each line last and when?
+

This is called the “annotate” or “blame” view. The name “blame” +is very unfortunate, but it is the standard term for historical reasons +for this functionality and it is not meant to blame anyone.

+
+

From a file view, change preview to “Blame” towards the top-left. +To get the actual commit, click on the commit message.

+
+
+
+
(6) Can you use this code yourself? Are you allowed to share modifications?
+
    +
  • Look at the file LICENSE.

  • +
  • On GitHub, click on the file to see a nice summary of what we can do with this:

    +
    +Screenshot on GitHub summarizing license terms +
    +
  • +
+
+
+
Summary
+
    +
  • Git allowed us to understand this simple project much better than we +could, if it was just a few files on our own computer.

  • +
  • It was easy to share the project with the course.

  • +
  • By forking the repository, we created our own copy. This is +important for the following, where we will make changes to +our copy.

  • +
+
+
+
+
+
Creating branches and commits
+

The first and most basic task to do in Git is record changes using +commits. In this part, we will record changes in two +ways: on a new branch (which supports multiple lines of work at once), and directly +on the “main” branch (which happens to be the default branch here).

+
+

Objectives

+
    +
  • Record new changes to our own copy of the project.

  • +
  • Understand adding changes in two separate branches.

  • +
  • See how to compare different versions.

  • +
+
+
+
Background
+
    +
  • In the previous episode we have browsed an existing repository and saw commits +and branches.

  • +
  • Each commit is a snapshot of the entire project at a certain +point in time and has a unique identifier (hash) .

  • +
  • A branch is a line of development, and the main branch or master branch +are often the default branch in Git.

  • +
  • A branch in Git is like a sticky note that is attached to a commit. When we add +new commits to a branch, the sticky note moves to the new commit.

  • +
  • Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

  • +
+
+Branching explained with a gopher +
+

What if two people, at the same time, make two different changes? Git +can merge them together easily. Image created using https://gopherize.me/ +(inspiration).

+
+
+
+
+
Exercise: Creating branches and commits
+
+Illustration of what we want to achieve in this exercise +
+

Illustration of what we want to achieve in this exercise.

+
+
+
+

Exercise: Practice creating commits and branches (20 min)

+
    +
  1. First create a new branch and then either add a new file or modify an +existing file and commit the change. Make sure that you now work on your +copy of the example repository. In your commit you can share a Git or +programming trick you like.

  2. +
  3. In a new commit, modify the file again.

  4. +
  5. Switch to the main branch and create a commit there.

  6. +
  7. Browse the network and locate the commits that you just created (“Insights” -> “Network”).

  8. +
  9. Compare the branch that you created with the main branch. Can you find an easy way to see the differences?

  10. +
  11. Can you find a way to compare versions between two arbitrary commits in the repository?

  12. +
  13. Try to rename the branch that you created and then browse the network again.

  14. +
  15. Try to create a tag for one of the commits that you created (on GitHub, +create a “release”).

  16. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+
Solution and walk-through
+
+
(1) Create a new branch and a new commit
+
+
    +
  1. Where it says “main” at the top left, click, enter a new branch +name new-tutorial, click on the offer to create the new branch +(“Create branch new-tutorial from main”).

  2. +
  3. Make sure you are still on the new-tutorial branch (it should say +it at the top), and click “Add file” → “Create new file” from the +upper right.

  4. +
  5. Enter a filename where it says “Name your file…”.

  6. +
  7. Share some Git or programming trick you like.

  8. +
  9. Click “Commit changes”

  10. +
  11. Enter a commit message. Then click “Commit +changes”.

  12. +
+

You should appear back at the file browser view, and see your +modification there.

+
+
+
+
(2) Modify the file again with a new commit
+
+

This is similar to before, but we click on the existing file to +modify.

+
    +
  1. Click on the file you added or modified previously.

  2. +
  3. Click the edit button, the pencil icon at top-right.

  4. +
  5. Follow the “Commit changes” instructions as in the previous step.

  6. +
+
+
+
+
(3) Switch to the main branch and create a commit there
+
+
    +
  1. Go back to the main repository page (your user’s page).

  2. +
  3. In the branch switch view (top left above the file view), switch to +main.

  4. +
  5. Modify another file that already exists, following the pattern +from above.

  6. +
+
+
+
+
(4) Browse the commits you just made
+

Let’s look at what we did. Now, the main and new-tutorial branches +have diverged: both have some modifications. Try to find the commits +you created.

+
+

Insights tab → Network view (just like we have done before).

+
+
+
+
(5) Compare the branches
+

Comparing changes is an important thing we need to do. When using the +GitHub view only, this may not be so common, but we’ll show it so that +it makes sense later on.

+
+

A nice way to compare branches is to add /compare to the URL of the repository, +for example (replace USER): +https://github.com/USER/planets/compare

+
+
+
+
(6) Compare two arbitrary commits
+

This is similar to above, but not only between branches.

+
+

Following the /compare-trick above, one can compare commits on GitHub by +adjusting the following URL: +https://github.com/USER/planets/compare/VERSION1..VERSION2

+

Replace USER with your username and VERSION1 and VERSION2 with a commit hash or branch name. +Please try it out.

+
+
+
+
(7) Renaming a branch
+
+

Branch button → View all branches → three dots at right side → Rename branch.

+
+
+
+
(8) Creating a tag
+

Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

+
+

On the right side, below “Releases”, click on “Create a new release”.

+

What GitHub calls releases are actually tags in Git with additional metadata. +For the purpose of this exercise we can use them interchangeably.

+
+
+
+
+
Summary
+

In this part, we saw how we can make changes to our files. With branches, we +can track several lines of work at once, and can compare their differences.

+
    +
  • You could commit directly to main if there is only one single line +of work and it’s only you.

  • +
  • You could commit to branches if there are multiple lines of work at +once, and you don’t want them to interfere with each other.

  • +
  • Tags are useful to mark a specific commit as important, for example a +release version.

  • +
  • In Git, commits form a so-called “graph”. Branches are tags in Git function +like sticky notes that stick to specific commits. What this means for us is +that it does not cost any significant disk space to create new branches.

  • +
  • Not all files should be added to Git. For example, temporary files or +files with sensitive information or files which are generated as part of +the build process should not be added to Git. For this we use +.gitignore (more about this later: Practical advice: How much Git is necessary?).

  • +
  • Unsure on which branch you are or what state the repository is in? +On the command line, use git status frequently to get a quick overview.

  • +
+
+
+
+
Practical advice: How much Git is necessary?
+
+
Writing useful commit messages
+

Useful commit messages summarize the change and provide context.

+

If you need a commit message that is longer than one line, +then the convention is: one line summarizing the commit, then one empty line, +then paragraph(s) with more details in free form, if necessary.

+

Good example:

+
increase alpha to 2.0 for faster convergence
+
+the motivation for this change is
+to enable ...
+...
+(more context)
+...
+this is based on a discussion in #123
+
+
+
    +
  • Why something was changed is more important than what has changed.

  • +
  • Cross-reference to issues and discussions if possible/relevant.

  • +
  • Bad commit messages: “fix”, “oops”, “save work”

  • +
  • Bad examples: http://whatthecommit.com

  • +
  • Write commit messages that will be understood +15 years from now by someone else than you. Or by your future you.

  • +
  • Many projects start out as projects “just for me” and end up to be successful projects +that are developed by 50 people over decades.

  • +
  • Commits with multiple authors are possible.

  • +
+

Good references:

+ +
+

Note

+

A great way to learn how to write commit messages and to get inspired by their +style choices: browse repositories of codes that you use/like:

+

Some examples (but there are so many good examples):

+ +

When designing commit message styles consider also these:

+
    +
  • How will you easily generate a changelog or release notes?

  • +
  • During code review, you can help each other improving commit messages.

  • +
+
+

But remember: it is better to make any commit, than no commit. Especially in small projects. +Let not the perfect be the enemy of the good enough.

+
+
+
What level of branching complexity is necessary for each project?
+

Simple personal projects:

+
    +
  • Typically start with just the main branch.

  • +
  • Use branches for unfinished/untested ideas.

  • +
  • Use branches when you are not sure about a change.

  • +
  • Use tags to mark important milestones.

  • +
  • If you are unsure what to do with unfinished and not working code, commit it +to a branch.

  • +
+

Projects with few persons: you accept things breaking sometimes

+
    +
  • It might be reasonable to commit to the main branch and feature branches.

  • +
+

Projects with few persons: changes are reviewed by others

+
    +
  • You create new feature branches for changes.

  • +
  • Changes are reviewed before they are merged to the main branch.

  • +
  • Consider to write-protect the main branch so that it can only be changed +with pull requests or merge requests.

  • +
+
+
+
How large should a commit be?
+
    +
  • Better too small than too large (easier to combine than to split).

  • +
  • Often I make a commit at the end of the day (this is a unit I would not like to lose).

  • +
  • Smaller sized commits may be easier to review for others than huge commits.

  • +
  • Imperfect commits are better than no commits.

  • +
  • A commit should not contain unrelated changes to simplify review and possible +repair/adjustments/undo later (but again: imperfect commits are better than no commits).

  • +
+
+
+
Working on the command line? Use “git status” all the time
+

The git status command is one of the most useful commands in Git +to inform about which branch we are on, what we are about to commit, +which files might not be tracked, etc.

+
+
+
How about staging and committing?
+
    +
  • Commit early and often: rather create too many commits than too few. +You can always combine commits later.

  • +
  • Once you commit, it is very, very hard to really lose your code.

  • +
  • Always fully commit (or stash) before you do dangerous things, so that you know you are safe. +Otherwise it can be hard to recover.

  • +
  • Later you can start using the staging area (where you first stage and then commit in a second step).

  • +
  • Later start using git add -p and/or git commit -p.

  • +
+
+
+
What to avoid
+
    +
  • Committing generated files/directories (example: __pycache__, *.pyc) -> +use .gitignore +files +(collection of .gitignore templates).

  • +
  • Committing huge files -> use code review to detect this.

  • +
  • Committing unrelated changes together.

  • +
  • Postponing commits because the changes are “unfinished”/”ugly” -> better ugly +commits than no commits.

  • +
  • When working with branches:

    +
      +
    • Not updating your branch before starting new work.

    • +
    • Too ambitious branch which risks to never get completed.

    • +
    • Over-engineering the branch layout and safeguards in small projects.

    • +
    • Working on unrelated things on the same branch.

    • +
    +
  • +
+
+
+
+
Optional: How to turn your project to a Git repo and share it
+
+

Objectives

+
    +
  • Turn our own coding project (small or large, finished or unfinished) into a +Git repository.

  • +
  • Be able to share a repository on the web to have a backup or so that others +can reuse and collaborate or even just find it.

  • +
+
+
+
Exercise
+
+Illustration of how we turn a project into a Git repository and share it +
+

From a bunch of files to a local repository which we then share on GitHub.

+
+
+
+

Exercise: Turn your project to a Git repo and share it (20 min)

+
    +
  1. Create a new directory called myproject with one or few files in it. +This represents our own project. It is not yet a Git repository. You can try +that with your own project or use a simple placeholder example.

  2. +
  3. Turn this new directory into a Git repository.

  4. +
  5. Share this repository on GitHub (or GitLab, since it really works the same).

  6. +
+

We offer three different paths of how to do this exercise.

+
    +
  • Via GitHub web interface: easy and can be a good starting point if you are completely +new to Git.

  • +
  • VS Code is quite easy, since VS Code can offer to create the +GitHub repositories for you.

  • +
  • Command line: you need to create the +repository on GitHub and link it yourself.

  • +
+
+

Create an repository on GitHub

+

First log into GitHub, then follow the screenshots and descriptions below.

+
+Screenshot on GitHub before a new repository form is opened +
+

Click on the “plus” symbol on top right, then on “New repository”.

+
+
+

Then:

+
+Screenshot on GitHub just before a new repository is created +
+

Choose a repository name, add a short description, and in this case make sure to check “Add a +README file”. Finally “Create repository”.

+
+
+

Upload your files

+

Now that the repository is created, you can upload your files:

+
+Screenshot on GitHub just before uploading files +
+

Click on the “+” symbol and then on “Upload files”.

+
+
+
+
+
+
+
Is putting software on GitHub/GitLab/… publishing?
+

It is a good first step but to make your code truly findable and +accessible, consider making your code citable and persistent: Get a +persistent identifier (PID) such as DOI in addition to sharing the code +publicly, by using services like Zenodo or similar +services.

+
+
+
+
+
+

Code documentation

+
+

Objectives

+
    +
  • Discuss what makes good documentation.

  • +
  • Improve the README of your project or our example project.

  • +
  • Explore Sphinx which is a popular tool to build documentation websites.

  • +
  • Learn how to leverage GitHub Actions and GitHub Pages to build and deploy documentation.

  • +
+
+
+

Instructor note

+
    +
  • (30 min) Discussion

  • +
  • (30 min) Exercise: Set up a Sphinx documentation and add API documentation

  • +
  • (15 min) Demo: Building documentation with GitHub Actions

  • +
+
+
+
Why? 💗✉️ to your future self
+
    +
  • You will probably use your code in the future and may forget details.

  • +
  • You may want others to use your code or contribute +(almost impossible without documentation).

  • +
+
+
+
In-code documentation
+

Not very useful (more commentary than comment):

+
# now we check if temperature is below -50
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

More useful (explaining why):

+
# we regard temperatures below -50 degrees as measurement errors
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

Keeping zombie code “just in case” (rather use version control):

+
# do not run this code!
+# if temperature > 0:
+#     print("It is warm")
+
+
+

Emulating version control:

+
# John Doe: threshold changed from 0 to 15 on August 5, 2013
+if temperature > 15:
+    print("It is warm")
+
+
+
+
+
Many languages allow “docstrings”
+

Example (Python):

+
def kelvin_to_celsius(temp_k: float) -> float:
+    """
+    Converts temperature in Kelvin to Celsius.
+
+    Parameters
+    ----------
+    temp_k : float
+        temperature in Kelvin
+
+    Returns
+    -------
+    temp_c : float
+        temperature in Celsius
+    """
+    assert temp_k >= 0.0, "ERROR: negative T_K"
+
+    temp_c = temp_k - 273.15
+
+    return temp_c
+
+
+
+

Keypoints

+
    +
  • Documentation which is only in the source code is not enough.

  • +
  • Often a README is enough.

  • +
  • Documentation needs to be kept +in the same Git repository as the code since we want it to evolve with +the code.

  • +
+
+
+
+
Often a README is enough - checklist
+
    +
  • Purpose

  • +
  • Requirements

  • +
  • Installation instructions

  • +
  • Copy-paste-able example to get started

  • +
  • Tutorials covering key functionality

  • +
  • Reference documentation (e.g. API) covering all functionality

  • +
  • Authors and recommended citation

  • +
  • License

  • +
  • Contribution guide

  • +
+

See also the +JOSS review checklist.

+
+
+
What if you need more than a README?
+ +
+
+
Diátaxis
+

Diátaxis is a systematic approach to technical documentation authoring.

+ +
+
+
Exercise: Set up a Sphinx documentation
+
+

Preparation

+

In this episode we will use the following 5 packages which we installed +previously as part of the Conda environment or Virtual environment:

+
myst-parser
+sphinx
+sphinx-rtd-theme
+sphinx-autoapi
+sphinx-autobuild
+
+
+

Which repository to use? You have 3 options:

+
    +
  • Clone your fork of the planets example repository.

  • +
  • If you don’t have that, you can clone the original repository itself: +https://github.com/workshop-material/planets

  • +
  • You can try this with your own project and the project does not have to +be a Python project.

  • +
+
+

There are at least two ways to get started with Sphinx:

+
    +
  1. Use sphinx-quickstart to create a new Sphinx project.

  2. +
  3. This is what we will do: Create three files (doc/conf.py, doc/index.md, and doc/about.md) +as starting point and improve from there.

  4. +
+
+

Exercise: Set up a Sphinx documentation

+
    +
  1. Create the following three files in your project:

    +
    planets/
    +├── doc/
    +│   ├── conf.py
    +│   ├── index.md
    +│   └── about.md
    +└── ...
    +
    +
    +

    This is conf.py:

    +
    project = "planets"
    +copyright = "2024, Authors"
    +author = "Authors"
    +release = "0.1"
    +
    +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
    +
    +extensions = [
    +    "myst_parser",  # in order to use markdown
    +]
    +
    +myst_enable_extensions = [
    +    "colon_fence",  # ::: can be used instead of ``` for better rendering
    +]
    +
    +html_theme = "sphinx_rtd_theme"
    +
    +
    +

    This is index.md (feel free to adjust):

    +
    # Our code documentation
    +
    +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
    +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
    +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
    +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
    +culpa qui officia deserunt mollit anim id est laborum.
    +
    +:::{toctree}
    +:maxdepth: 2
    +:caption: Some caption
    +
    +about.md
    +:::
    +
    +
    +

    This is about.md (feel free to adjust):

    +
    # About this code
    +
    +Work in progress ...
    +
    +
    +
  2. +
  3. Run sphinx-build to build the HTML documentation:

    +
    $ sphinx-build doc _build
    +
    +... lots of output ...
    +The HTML pages are in _build.
    +
    +
    +
  4. +
  5. Try to open _build/index.html in your browser.

  6. +
  7. Experiment with adding more content, images, equations, code blocks, …

    + +
  8. +
+
+

There is a lot more you can do:

+
    +
  • This is useful if you want to check the integrity of all internal and external links:

    +
    $ sphinx-build doc -W -b linkcheck _build
    +
    +
    +
  • +
  • sphinx-autobuild +provides a local web server that will automatically refresh your view +every time you save a file - which makes writing and testing much easier.

  • +
+
+
+
Demo: Building documentation with GitHub Actions
+

First we need to extend the environment.yml file to include the necessary packages:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - myst-parser
+  - sphinx
+  - sphinx-rtd-theme
+  - sphinx-autoapi
+
+
+

Then we add a GitHub Actions workflow .github/workflow/sphinx.yml to build the documentation:

+
name: Build documentation
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+permissions:
+  contents: write
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Sphinx build
+      run: |
+        sphinx-build doc _build
+      shell: bash -el {0}
+
+    - name: Deploy to GitHub Pages
+      uses: peaceiris/actions-gh-pages@v4
+      if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+      with:
+        publish_branch: gh-pages
+        github_token: ${{ secrets.GITHUB_TOKEN }}
+        publish_dir: _build/
+        force_orphan: true
+
+
+

Now:

+
    +
  • Add these two changes to the GitHub repository.

  • +
  • Go to “Settings” -> “Pages” -> “Branch” -> gh-pages -> “Save”.

  • +
  • Look at “Actions” tab and observe the workflow running and hopefully +deploying the website.

  • +
  • Finally visit the generated site. You find it by clicking the About wheel +icon on top right of your repository. There, select “Use your GitHub Pages +website”.

  • +
  • This is how we build almost all of our lesson websites (including this one).

  • +
  • Another popular place to deploy Sphinx documentation is ReadTheDocs.

  • +
+
+
+
Optional: How to auto-generate API documentation in Python
+

Add three tiny modifications (highlighted) to doc/conf.py to auto-generate API documentation +(this requires the sphinx-autoapi package):

+
project = "planets"
+copyright = "2024, Authors"
+author = "Authors"
+release = "0.1"
+
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+extensions = [
+    "myst_parser",  # in order to use markdown
+    "autoapi.extension",  # in order to use markdown
+]
+
+# search this directory for Python files
+autoapi_dirs = [".."]
+
+# ignore this file when generating API documentation
+autoapi_ignore = ["*/conf.py"]
+
+myst_enable_extensions = [
+    "colon_fence",  # ::: can be used instead of ``` for better rendering
+]
+
+html_theme = "sphinx_rtd_theme"
+
+
+

Then rebuild the documentation (or push the changes and let GitHub rebuild it) +and you should see a new section “API Reference”.

+
+
+
Confused about reStructuredText vs. Markdown vs. MyST?
+
    +
  • At the beginning there was reStructuredText and Sphinx was built for reStructuredText.

  • +
  • Independently, Markdown was invented and evolved into a couple of flavors.

  • +
  • Markdown became more and more popular but was limited compared to reStructuredText.

  • +
  • Later, MyST +was invented to be able to write +something that looks like Markdown but in addition can do everything that +reStructuredText can do with extra directives.

  • +
+
+
+
Where to read more
+ +
+
+
+

Collaborative version control and code review

+
+
+
Concepts around collaboration
+
+

Objectives

+
    +
  • Be able to decide whether to divide work at the branch level or at the repository level.

  • +
+
+
+
Commits, branches, repositories, forks, clones
+
    +
  • repository: The project, contains all data and history (commits, branches, tags).

  • +
  • commit: Snapshot of the project, gets a unique identifier (e.g. c7f0e8bfc718be04525847fc7ac237f470add76e).

  • +
  • branch: Independent development line. The main development line is often called main.

  • +
  • tag: A pointer to one commit, to be able to refer to it later. Like a commemorative plaque +that you attach to a particular commit (e.g. phd-printed or paper-submitted).

  • +
  • cloning: Copying the whole repository to your laptop - the first time. It is not necessary to download each file one by one.

  • +
  • forking: Taking a copy of a repository (which is typically not yours) - your +copy (fork) stays on GitHub/GitLab and you can make changes to your copy.

  • +
+
+
+
Cloning a repository
+

In order to make a complete copy a whole repository, the git clone command +can be used. When cloning, all the files, of all or selected branches, of a +repository are copied in one operation. Cloning of a repository is of relevance +in a few different situations:

+
    +
  • Working on your own, cloning is the operation that you can use to create +multiple instances of a repository on, for instance, a personal computer, a +server, and a supercomputer.

  • +
  • The parent repository could be a repository that you or your colleague own. A +common use case for cloning is when working together within a smaller team +where everyone has read and write access to the same git repository.

  • +
  • Alternatively, cloning can be made from a public repository of a code that +you would like to use. Perhaps you have no intention to work on the code, but +would like to stay in tune with the latest developments, also in-between +releases of new versions of the code.

  • +
+
+Forking and cloning +
+

Forking and cloning

+
+
+
+
+
Forking a repository
+

When a fork is made on GitHub/GitLab a complete copy, of all or selected +branches, of the repository is made. The copy will reside under a different +account on GitHub/GitLab. Forking of a repository is of high relevance when +working with a git repository to which you do not have write access.

+
    +
  • In the fork repository commits can be made to the base branch (main or +master), and to other branches.

  • +
  • The commits that are made within the branches of the fork repository can be +contributed back to the parent repository by means of pull or merge requests.

  • +
+
+
+
Synchronizing changes between repositories
+
    +
  • We need a mechanism to communicate changes between the repositories.

  • +
  • We will pull or fetch updates from remote repositories (we will soon discuss the difference between pull and fetch).

  • +
  • We will push updates to remote repositories.

  • +
  • We will learn how to suggest changes within repositories on GitHub and across repositories (pull request).

  • +
  • Repositories that are forked or cloned do not automatically synchronize themselves: +We will learn how to update forks (by pulling from the “central” repository).

  • +
  • A main difference between cloning a repository and forking a repository is that the former is a general operation for generating copies of a repository to different computers, whereas forking is a particular operation implemented on GitHub/GitLab.

  • +
+
+
+
+
Collaborating within the same repository
+

In this episode, we will learn how to collaborate within the same repository. +We will learn how to cross-reference issues and pull requests, how to review +pull requests, and how to use draft pull requests.

+

This exercise will form a good basis for collaboration that is suitable for +most research groups.

+
+

Note

+

When you read or hear pull request, please think of a change proposal.

+
+
+
Exercise
+

In this exercise, we will contribute to a repository via a pull request. +This means that you propose some change, and then it is accepted (or not).

+
+

Exercise preparation

+
    +
  • First we need to get access to the exercise repository to which we will +contribute.

    +
      +
    • Instructor collects GitHub usernames from learners and adds them as collaborators to the exercise repository +(Settings -> Collaborators and teams -> Manage access -> Add people).

    • +
    +
  • +
  • Don’t forget to accept the invitation

    +
      +
    • Check https://github.com/settings/organizations/

    • +
    • Alternatively check the inbox for the email account you registered with +GitHub. GitHub emails you an invitation link, but if you don’t receive it +you can go to your GitHub notifications in the top right corner. The +maintainer can also “copy invite link” and share it within the group.

    • +
    +
  • +
  • Watching and unwatching repositories

    +
      +
    • Now that you are a collaborator, you get notified about new issues and pull +requests via email.

    • +
    • If you do not wish this, you can “unwatch” a repository (top of +the project page).

    • +
    • However, we recommend watching repositories you are interested +in. You can learn things from experts just by watching the +activity that come through a popular project.

    • +
    +
    +Unwatching a repository +
    +

    Unwatch a repository by clicking “Unwatch” in the repository view, +then “Participating and @mentions” - this way, you will get +notifications about your own interactions.

    +
    +
    +
  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements (from installation instructions):

+ +

What is familiar from the previous workshop day (not repeated here):

+
    +
  • Cloning a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Submit a pull request towards the main branch.

  • +
+

What will be new in this exercise:

+
    +
  • If you create the changes locally, you will need to push them to the remote repository.

  • +
  • Learning what a protected branch is and how to modify a protected branch: using a pull request.

  • +
  • Cross-referencing issues and pull requests.

  • +
  • Practice to review a pull request.

  • +
  • Learn about the value of draft pull requests.

  • +
+

Exercise tasks:

+
    +
  1. Start in the exercise +repository and open an +issue where you describe the change you want to make. Note down the issue +number since you will need it later.

  2. +
  3. Create a new branch.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit +cross-reference the issue you opened (see the walk-through below +for how to do that).

  6. +
  7. Push your new branch (with the new commit) to the repository you +are working on.

  8. +
  9. Open a pull request towards the main branch.

  10. +
  11. Review somebody else’s pull request and give constructive feedback. Merge their pull request.

  12. +
  13. Try to create a new branch with some half-finished work and open a draft +pull request. Verify that the draft pull request cannot be merged since it +is not meant to be merged yet.

  14. +
+
+
+
+
Solution and hints
+
+
(1) Opening an issue
+

This is done through the GitHub web interface. For example, you could +give the name of the recipe you want to add (so that others don’t add +the same one). It is the “Issues” tab.

+
+
+
(2) Create a new branch.
+

If on GitHub, you can make the branch in the web interface.

+
+
+
(3) Make a change adding the recipe
+

Add a new file with the recipe in it. Commit the file. In the commit +message, include the note about the issue number, saying that this +will close that issue.

+
+
Cross-referencing issues and pull requests
+

Each issue and each pull request gets a number and you can cross-reference them.

+

When you open an issue, note down the issue number (in this case it is #2):

+
+Each issue gets a number +
+

You can reference this issue number in a commit message or in a pull request, like in this +commit message:

+
this is the new recipe; fixes #2
+
+
+

If you forget to do that in your commit message, you can also reference the issue +in the pull request description. And instead of fixes you can also use closes or resolves +or fix or close or resolve (case insensitive).

+

Here are all the keywords that GitHub recognizes: +https://help.github.com/en/articles/closing-issues-using-keywords

+

Then observe what happens in the issue once your commit gets merged: it will +automatically close the issue and create a link between the issue and the +commit. This is very useful for tracking what changes were made in response to +which issue and to know from when until when precisely the issue was open.

+
+
+
+
(4) Push to GitHub as a new branch
+

Push the branch to the repository. You should end up with a branch +visible in the GitHub web view.

+

This is only necessary if you created the changes locally. If you created the +changes directly on GitHub, you can skip this step.

+
+

In VS Code, you can “publish the branch” to the remote repository by clicking +the cloud icon in the bottom left corner of the window:

+
+Publishing a branch in VS Code +
+
+
+
+
(5) Open a pull request towards the main branch
+

This is done through the GitHub web interface.

+
+
+
(6) Reviewing pull requests
+

You review through the GitHub web interface.

+

Checklist for reviewing a pull request:

+
    +
  • Be kind, on the other side is a human who has put effort into this.

  • +
  • Be constructive: if you see a problem, suggest a solution.

  • +
  • Towards which branch is this directed?

  • +
  • Is the title descriptive?

  • +
  • Is the description informative?

  • +
  • Scroll down to see commits.

  • +
  • Scroll down to see the changes.

  • +
  • If you get incredibly many changes, also consider the license or copyright +and ask where all that code is coming from.

  • +
  • Again, be kind and constructive.

  • +
  • Later we will learn how to suggest changes directly in the pull request.

  • +
+

If someone is new, it’s often nice to say something encouraging in the +comments before merging (even if it’s just “thanks”). If all is good +and there’s not much else to say, you could merge directly.

+
+
+
(7) Draft pull requests
+

Try to create a draft pull request:

+
+Creating a draft pull request +
+

Verify that the draft pull request cannot be merged until it is marked as ready +for review:

+
+Draft pull request cannot be merged +
+

Draft pull requests can be useful for:

+
    +
  • Feedback: You can open a pull request early to get feedback on your work without +signaling that it is ready to merge.

  • +
  • Information: They can help communicating to others that a change is coming up and in +progress.

  • +
+
+
+
What is a protected branch? And how to modify it?
+

A protected branch on GitHub or GitLab is a branch that cannot (accidentally) +deleted or force-pushed to. It is also possible to require that a branch cannot +be directly pushed to or modified, but that changes must be submitted via a +pull request.

+

To protect a branch in your own repository, go to “Settings” -> “Branches”.

+
+
+
Summary
+
    +
  • We used all the same pieces that we’ve learned previously.

  • +
  • But we successfully contributed to a collaborative project!

  • +
  • The pull request allowed us to contribute without changing directly: +this is very good when it’s not mainly our project.

  • +
+
+
+
+
+
Practicing code review
+

In this episode we will practice the code review process. We will learn how to +ask for changes in a pull request, how to suggest a change in a pull request, +and how to modify a pull request.

+

This will enable research groups to work more collaboratively and to not only +improve the code quality but also to learn from each other.

+
+
Exercise
+
+

Exercise preparation

+

We can continue in the same exercise repository which we have used in the +previous episode.

+
+
+

Exercise: Practicing code review (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • As a reviewer, we will learn how to ask for changes in a pull request.

  • +
  • As a reviewer, we will learn how to suggest a change in a pull request.

  • +
  • As a submitter, we will learn how to modify a pull request without closing +the incomplete one and opening a new one.

  • +
+

Exercise tasks:

+
    +
  1. Create a new branch and one or few commits: in these improve something but also +deliberately introduce a typo and also a larger mistake which we will want to fix during the code review.

  2. +
  3. Open a pull request towards the main branch.

  4. +
  5. As a reviewer to somebody else’s pull request, ask for an improvement and +also directly suggest a change for the small typo. (Hint: +suggestions are possible through the GitHub web interface, view of +a pull request, “Files changed” view, after selecting some lines. +Look for the “±” button.)

  6. +
  7. As the submitter, learn how to accept the suggested change. (Hint: +GitHub web interface, “Files Changed” view.)

  8. +
  9. As the submitter, improve the pull request without having to close and open +a new one: by adding a new commit to the same branch. (Hint: push +to the branch again.)

  10. +
  11. Once the changes are addressed, merge the pull request.

  12. +
+
+
+
+
Help and discussion
+

From here on out, we don’t give detailed steps to the solution. You +need to combine what you know, and the extra info below, in order to +solve the above.

+
+
How to ask for changes in a pull request
+

Technically, there are at least two common ways to ask for changes in a pull +request.

+

Either in the comment field of the pull request:

+
+Screenshot of a pull request comment field +
+

Or by using the “Review changes”:

+
+Screenshot of a pull request navigating to the "Review changes" tab +
+

And always please be kind and constructive in your comments. Remember that the +goal is not gate-keeping but collaborative learning.

+
+
+
How to suggest a change in a pull request as a reviewer
+

If you see a very small problem that is easy to fix, you can suggest a change +as a reviewer.

+

Instead of asking the submitter to tiny problem, you can suggest a change by +clicking on the plus sign next to the line number in the “Files changed” tab:

+
+Screenshot of leaving a comment to a line in a pull request +
+

Here you can comment on specific lines or even line ranges.

+

But now the interesting part is to click on the “Add a suggestion” symbol (the +one that looks like plus and minus). Now you can fix the tiny problem (in this +case a typo) and then click on the “Add single comment” button:

+
+Sequence of clicks to add a suggestion to a line in a pull request +
+

The result is this and the submitter can accept the change with a single click:

+
+Screenshot of a pull request with a suggested change +
+

After accepting with “Commit suggestion”, the improvement gets added to the +pull request.

+
+
+
How to modify a pull request to address the review comments
+

If the reviewer asks for changes, it is not necessary to close the pull request +and later open a new one. It can even be counter-productive to do so: This can +fragment the discussion and the history of the pull request and can make it +harder to understand the context of the changes.

+

A much better mechanism to recognize that pull requests are not implemented +from a specific commit to a specific branch, but always from a branch to a +branch.

+

This means that you can make amendments to the pull request by adding new +commits to the same source branch. This way the pull request will be updated +automatically and the reviewer can see the new changes and comment on them.

+

The fact that pull requests are from branch to branch also strongly suggests +that it is a good practice to create a new branch for each pull request. +Otherwise you could accidentally modify an open pull request by adding new +commits to the source branch.

+
+
+
+
Summary
+
    +
  • Our process isn’t just about code now. It’s about discussion and +working together to make the whole process better.

  • +
  • GitHub discussions and reviewing are quite powerful and can make +small changes easy.

  • +
+
+
+
+
How to contribute changes to repositories that belong to others
+

In this episode we prepare you to suggest and contribute changes to +repositories that belong to others. These might be open source projects that +you use in your work.

+

We will see how Git and services like GitHub or GitLab can be used to suggest +modification without having to ask for write access to the repository and +accept modifications without having to grant write access to others.

+
+
Exercise
+
+

Exercise preparation

+
    +
  • The exercise repository is now different: +https://github.com/workshop-material/recipe-book-forking-exercise (note the -forking-exercise).

  • +
  • First fork the exercise repository to your GitHub account.

  • +
  • Then clone your fork to your computer (if you wish to work locally).

  • +
  • Double-check that you have forked the correct repository.

  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Forking a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • Opening a pull request towards the upstream repository.

  • +
  • Pull requests can be coupled with automated testing.

  • +
  • Learning that your fork can get out of date.

  • +
  • After the pull requests are merged, updating your fork with the changes.

  • +
  • Learn how to approach other people’s repositories with ideas, changes, and requests.

  • +
+

Exercise tasks:

+
    +
  1. Open an issue in the upstream exercise repository where you describe the +change you want to make. Take note of the issue number.

  2. +
  3. Create a new branch in your fork of the repository.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit cross-reference the issue you opened. +See the walk-through below for how to do this.

  6. +
  7. Open a pull request towards the upstream repository.

  8. +
  9. The instructor will review and merge the pull requests. +During the review, pay attention to the automated test step (here for +demonstration purposes, we test whether the recipe contains an ingredients +and an instructions sections).

  10. +
  11. After few pull requests are merged, update your fork with the changes.

  12. +
  13. Check that in your fork you can see changes from other people’s pull requests.

  14. +
+
+
+
+
Help and discussion
+
+
Help! I don’t have permissions to push my local changes
+

Maybe you see an error like this one:

+
Please make sure you have the correct access rights
+and the repository exists.
+
+
+

Or like this one:

+
failed to push some refs to workshop-material/recipe-book-forking-exercise.git
+
+
+

In this case you probably try to push the changes not to your fork but to the original repository +and in this exercise you do not have write access to the original repository.

+

The simpler solution is to clone again but this time your fork.

+ +
+
+
Opening a pull request towards the upstream repository
+

We have learned in the previous episode that pull requests are always from +branch to branch. But the branch can be in a different repository.

+

When you open a pull request in a fork, by default GitHub will suggest to +direct it towards the default branch of the upstream repository.

+

This can be changed and it should always be verified, but in this case this is +exactly what we want to do, from fork towards upstream:

+
+Screenshot of a pull request from fork towards upstream +
+
+
+
Pull requests can be coupled with automated testing
+

We added an automated test here just for fun and so that you see that this is +possible to do.

+

In this exercise, the test is silly. It will check whether the recipe contains +both an ingredients and an instructions section.

+

In this example the test failed:

+
+Screenshot of a failed test in a pull request +
+

Click on the “Details” link to see the details of the failed test:

+
+Screenshot of details why the test failed +
+

How can this be useful?

+
    +
  • The project can define what kind of tests are expected to pass before a pull +request can be merged.

  • +
  • The reviewer can see the results of the tests, without having to run them +locally.

  • +
+

How does it work?

+
    +
  • We added a GitHub Actions workflow +to automatically run on each push or pull request towards the main branch.

  • +
+

What tests or steps can you image for your project to run automatically with +each pull request?

+
+
+
How to update your fork with changes from upstream
+

This used to be difficult but now it is two mouse clicks.

+

Navigate to your fork and notice how GitHub tells you that your fork is behind. +In my case, it is 9 commits behind upstream. To fix this, click on “Sync fork” +and then “Update branch”:

+
+Screenshot on GitHub fork page showing that the fork is behind +
+

After the update my “branch is up to date” with the upstream repository:

+
+Screenshot on GitHub after fork has been updated +
+
+
+
How to approach other people’s repositories with ideas, changes, and requests
+

Contributing very minor changes

+
    +
  • Clone or fork+clone repository

  • +
  • Create a branch

  • +
  • Commit and push change

  • +
  • Open a pull request or merge request

  • +
+

If you observe an issue and have an idea how to fix it

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • Describe the problem

  • +
  • If you have a suggestion on how to fix it, describe your suggestion

  • +
  • Possibly discuss and get feedback

  • +
  • If you are working on the fix, indicate it in the issue so that others know that somebody is working on it and who is working on it

  • +
  • Submit your fix as pull request or merge request which references/closes the issue

  • +
+
+

Motivation

+
    +
  • Inform others about an observed problem

  • +
  • Make it clear whether this issue is up for grabs or already being worked on

  • +
+
+

If you have an idea for a new feature

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • In the issue, write a short proposal for your suggested change or new feature

  • +
  • Motivate why and how you wish to do this

  • +
  • Also indicate where you are unsure and where you would like feedback

  • +
  • Discuss and get feedback before you code

  • +
  • Once you start coding, indicate that you are working on it

  • +
  • Once you are done, submit your new feature as pull request or merge request which references/closes the issue/proposal

  • +
+
+

Motivation

+
    +
  • Get agreement and feedback before writing 5000 lines of code which might be rejected

  • +
  • If we later wonder why something was done, we have the issue/proposal as +reference and can read up on the reasoning behind a code change

  • +
+
+
+
+
+
Summary
+
    +
  • This forking workflow lets you propose changes to repositories for +which you have no write access.

  • +
  • This is the way that much modern open-source software works.

  • +
  • You can now contribute to any project you can view.

  • +
+
+
+
+
Conclict resolution, rebasing, and organizational strategies
+

(40 min demo and discussion)

+
+
+
+
+

Automated testing

+
+

Objectives

+
    +
  • Know where to start in your own project.

  • +
  • Have an example for how to make the testing part of code review.

  • +
+
+
+

Instructor note

+
    +
  • (15 min) Motivation

  • +
  • (15 min) End-to-end tests

  • +
  • (15 min) Pytest

  • +
  • (15 min) Adding the unit test to GitHub Actions

  • +
  • (10 min) What else is possible

  • +
  • (20 min) Exercise

  • +
+
+
+
Motivation
+

Testing is a way to check that the code does what it is expected to.

+
    +
  • Less scary to change code: tests will tell you whether something broke.

  • +
  • Easier for new people to join.

  • +
  • Easier for somebody to revive an old code.

  • +
  • End-to-end test: run the whole code and compare result to a reference.

  • +
  • Unit tests: test one unit (function or module). Can guide towards better +structured code: complicated code is more difficult to test.

  • +
+
+
+
How testing is often taught
+
def add(a, b):
+    return a + b
+
+
+def test_add():
+    assert add(1, 2) == 3
+
+
+

How this feels:

+
+Instruction on how to draw an owl +
+

[Citation needed]

+
+
+

Instead, we will look at and discuss a real example where we test components +from our example project.

+
+
+
Where to start
+

Short answer: Start with an end-to-end test.

+ +
+
+
End-to-end tests
+ +
+

Discussion

+

Is the end-to-end test +perfect? No. But it’s a good starting point. Discuss its limitations.

+
+
+
+
Pytest
+

First we need to add a test function, for instance +for this function:

+
def force_between_planets(position1, mass1, position2, mass2):
+    G = 1.0  # gravitational constant
+
+    r = position2 - position1
+    distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5
+    force_magnitude = G * mass1 * mass2 / distance**2
+    force = (r / distance) * force_magnitude
+
+    return force
+
+
+def test_force_between_planets():
+    position1 = np.array([0.0, 0.0, 0.0])
+    mass1 = 1.0
+    position2 = np.array([1.0, 0.0, 0.0])
+    mass2 = 2.0
+
+    force = force_between_planets(position1, mass1, position2, mass2)
+
+    assert np.allclose(force, [2.0, 0.0, 0.0])
+
+
+

Let us run the test with:

+
$ pytest simulate.py
+
+
+

Explanation: pytest will look for functions starting with test_ in files +and directories given as arguments. It will run them and report the results.

+

Now let us try this:

+
    +
  • Commit the test.

  • +
  • Break the function on purpose and run the test.

  • +
  • Does the test fail as expected?

  • +
+
+
+
Adding the unit test to GitHub Actions
+

Our next goal is that we want GitHub to run the unit test +automatically on every change.

+

First we need to extend our +environment.yml:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - pytest
+
+
+

We also need to extend .github/workflows/test.yml (highlighted line):

+
name: Test
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Run tests
+      run: |
+        ./test.sh
+        pytest simulate.py
+      shell: bash -el {0}
+
+
+

If we have time, we can try to create a pull request which would break the +code and see how the test fails.

+
+
+
What else is possible
+
    +
  • The testing above used example-based testing.

  • +
  • Test coverage: how much of the code is traversed by tests?

    + +
  • +
  • Property-based testing: generates arbitrary data matching your specification and checks that your guarantee still holds in that case.

    + +
  • +
  • Snapshot-based testing: makes it easier to generate snapshots for regression tests.

    + +
  • +
  • Mutation testing: tests pass -> change a line of code (make a mutant) -> test again and check whether all mutants get “killed”.

    + +
  • +
+
+
+
Exercises
+

Experiment with the example project and what we learned above or try it on your +own project:

+
    +
  • Add a unit test.

  • +
  • Try to run it locally.

  • +
  • Check whether it fails when you break the corresponding function.

  • +
  • Try to run it on GitHub Actions.

  • +
  • Create a pull request which would break the code and see whether the automatic test would catch it.

  • +
  • Try to design an end-to-end test for your project.

  • +
+
+
+
+

How to make the project more reusable

+

Part 1:

+
    +
  • 15 min: Motivation

  • +
  • 15 min: Best practices for managing dependencies in Python

  • +
  • 20 min: Exercise

  • +
+

Part 2:

+
    +
  • 10 min: Introduction to containers

  • +
  • 20 min: Demonstration

  • +
  • 10 min: Discussion

  • +
+
+
+

Demo: From a script towards a workflow

+

In this episode we will explore code quality and good practices in Python using +a hands-on approach. We will together build up a small project and improve it +step by step.

+

We will start from a relatively simple image processing script which can read a +telescope image of stars and our goal is to count the number of stars in +the image. Later we will want to be able to process many such images.

+

The (fictional) telescope images look like the one below here (in this +repository we can find more):

+
+Generated image representing a telescope image of stars +
+

Generated image representing a telescope image of stars.

+
+
+
+

Rough plan for this demo

+
    +
  • (15 min) Discuss how we would solve the problem, run example code, and make it work (as part of a Jupyter notebook)?

  • +
  • (15 min) Refactor the positioning code into a function and a module

  • +
  • (15 min) Now we wish to process many images - discuss how we would approach this

  • +
  • (15 min) Introduce CLI and discuss the benefits

  • +
  • (30 min) From a script to a workflow (using Snakemake)

  • +
+
+ +
+
Plan
+

Topics we wish to show and discuss:

+
    +
  • Naming (and other) conventions, project organization, modularity

  • +
  • The value of pure functions and immutability

  • +
  • Refactoring (explained through examples)

  • +
  • Auto-formatting and linting with tools like black, vulture, ruff

  • +
  • Moving a project under Git

  • +
  • How to document dependencies

  • +
  • Structuring larger software projects in a modular way

  • +
  • Command-line interfaces

  • +
  • Workflows with Snakemake

  • +
+

We will work together on the code on the big screen, and participants will be +encouraged to give suggestions and ask questions. We will end up with a Git +repository which will be shared with workshop participants.

+
+
+
Possible solutions
+ + +
+
+
+

Concepts in refactoring and modular code design

+

Topics:

+
    +
  • Pure functions

  • +
  • Design patterns: functional design vs. object-oriented design

  • +
  • How to design your code before writing it: document-driven development

  • +
+
+
+

How to release and publish your code

+

Part 1:

+
    +
  • 30 min: Overview over software licenses and code publishing

  • +
  • 15 min: We add a license, CFF file, and publish to Zenodo (sandbox)

  • +
+

Part 2:

+
    +
  • 30 min: Creating a Python package with the help of flit

  • +
  • 15 min: Deploy the package to test-PyPI using GitHub Actions

  • +
+
+
+

Profiling memory and CPU usage

+

Profilers:

+
    +
  • cProfile or profile

  • +
  • line_profiler

  • +
  • viztracer

  • +
  • scalene

  • +
  • Intel VTune

  • +
  • PyInstrument

  • +
  • py-spy

  • +
  • Pyflame

  • +
  • timeit

  • +
+

(here I will add some examples that we can profile together)

+
+
+
+
+

Credit

+

The following material (all CC-BY) was reused to create this workshop material:

+ +

(I will also summarize what changes have been made to the original material)

+
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright CodeRefinery team (CC-BY license).

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/singlehtml/objects.inv b/branch/main/singlehtml/objects.inv new file mode 100644 index 0000000..636e87a Binary files /dev/null and b/branch/main/singlehtml/objects.inv differ diff --git a/branch/main/testing/index.html b/branch/main/testing/index.html new file mode 100644 index 0000000..1ec8c5e --- /dev/null +++ b/branch/main/testing/index.html @@ -0,0 +1,379 @@ + + + + + + + Automated testing — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Automated testing

+
+

Objectives

+
    +
  • Know where to start in your own project.

  • +
  • Have an example for how to make the testing part of code review.

  • +
+
+
+

Instructor note

+
    +
  • (15 min) Motivation

  • +
  • (15 min) End-to-end tests

  • +
  • (15 min) Pytest

  • +
  • (15 min) Adding the unit test to GitHub Actions

  • +
  • (10 min) What else is possible

  • +
  • (20 min) Exercise

  • +
+
+
+

Motivation

+

Testing is a way to check that the code does what it is expected to.

+
    +
  • Less scary to change code: tests will tell you whether something broke.

  • +
  • Easier for new people to join.

  • +
  • Easier for somebody to revive an old code.

  • +
  • End-to-end test: run the whole code and compare result to a reference.

  • +
  • Unit tests: test one unit (function or module). Can guide towards better +structured code: complicated code is more difficult to test.

  • +
+
+
+

How testing is often taught

+
def add(a, b):
+    return a + b
+
+
+def test_add():
+    assert add(1, 2) == 3
+
+
+

How this feels:

+
+Instruction on how to draw an owl +
+

[Citation needed]

+
+
+

Instead, we will look at and discuss a real example where we test components +from our example project.

+
+
+

Where to start

+

Short answer: Start with an end-to-end test.

+ +
+
+

End-to-end tests

+ +
+

Discussion

+

Is the end-to-end test +perfect? No. But it’s a good starting point. Discuss its limitations.

+
+
+
+

Pytest

+

First we need to add a test function, for instance +for this function:

+
def force_between_planets(position1, mass1, position2, mass2):
+    G = 1.0  # gravitational constant
+
+    r = position2 - position1
+    distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5
+    force_magnitude = G * mass1 * mass2 / distance**2
+    force = (r / distance) * force_magnitude
+
+    return force
+
+
+def test_force_between_planets():
+    position1 = np.array([0.0, 0.0, 0.0])
+    mass1 = 1.0
+    position2 = np.array([1.0, 0.0, 0.0])
+    mass2 = 2.0
+
+    force = force_between_planets(position1, mass1, position2, mass2)
+
+    assert np.allclose(force, [2.0, 0.0, 0.0])
+
+
+

Let us run the test with:

+
$ pytest simulate.py
+
+
+

Explanation: pytest will look for functions starting with test_ in files +and directories given as arguments. It will run them and report the results.

+

Now let us try this:

+
    +
  • Commit the test.

  • +
  • Break the function on purpose and run the test.

  • +
  • Does the test fail as expected?

  • +
+
+
+

Adding the unit test to GitHub Actions

+

Our next goal is that we want GitHub to run the unit test +automatically on every change.

+

First we need to extend our +environment.yml:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - pytest
+
+
+

We also need to extend .github/workflows/test.yml (highlighted line):

+
name: Test
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Run tests
+      run: |
+        ./test.sh
+        pytest simulate.py
+      shell: bash -el {0}
+
+
+

If we have time, we can try to create a pull request which would break the +code and see how the test fails.

+
+
+

What else is possible

+
    +
  • The testing above used example-based testing.

  • +
  • Test coverage: how much of the code is traversed by tests?

    + +
  • +
  • Property-based testing: generates arbitrary data matching your specification and checks that your guarantee still holds in that case.

    + +
  • +
  • Snapshot-based testing: makes it easier to generate snapshots for regression tests.

    + +
  • +
  • Mutation testing: tests pass -> change a line of code (make a mutant) -> test again and check whether all mutants get “killed”.

    + +
  • +
+
+
+

Exercises

+

Experiment with the example project and what we learned above or try it on your +own project:

+
    +
  • Add a unit test.

  • +
  • Try to run it locally.

  • +
  • Check whether it fails when you break the corresponding function.

  • +
  • Try to run it on GitHub Actions.

  • +
  • Create a pull request which would break the code and see whether the automatic test would catch it.

  • +
  • Try to design an end-to-end test for your project.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/version-control/branching-and-committing/index.html b/branch/main/version-control/branching-and-committing/index.html new file mode 100644 index 0000000..2827392 --- /dev/null +++ b/branch/main/version-control/branching-and-committing/index.html @@ -0,0 +1,432 @@ + + + + + + + Creating branches and commits — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Creating branches and commits

+

The first and most basic task to do in Git is record changes using +commits. In this part, we will record changes in two +ways: on a new branch (which supports multiple lines of work at once), and directly +on the “main” branch (which happens to be the default branch here).

+
+

Objectives

+
    +
  • Record new changes to our own copy of the project.

  • +
  • Understand adding changes in two separate branches.

  • +
  • See how to compare different versions.

  • +
+
+
+

Background

+
    +
  • In the previous episode we have browsed an existing repository and saw commits +and branches.

  • +
  • Each commit is a snapshot of the entire project at a certain +point in time and has a unique identifier (hash) .

  • +
  • A branch is a line of development, and the main branch or master branch +are often the default branch in Git.

  • +
  • A branch in Git is like a sticky note that is attached to a commit. When we add +new commits to a branch, the sticky note moves to the new commit.

  • +
  • Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

  • +
+
+Branching explained with a gopher +
+

What if two people, at the same time, make two different changes? Git +can merge them together easily. Image created using https://gopherize.me/ +(inspiration).

+
+
+
+
+

Exercise: Creating branches and commits

+
+Illustration of what we want to achieve in this exercise +
+

Illustration of what we want to achieve in this exercise.

+
+
+
+

Exercise: Practice creating commits and branches (20 min)

+
    +
  1. First create a new branch and then either add a new file or modify an +existing file and commit the change. Make sure that you now work on your +copy of the example repository. In your commit you can share a Git or +programming trick you like.

  2. +
  3. In a new commit, modify the file again.

  4. +
  5. Switch to the main branch and create a commit there.

  6. +
  7. Browse the network and locate the commits that you just created (“Insights” -> “Network”).

  8. +
  9. Compare the branch that you created with the main branch. Can you find an easy way to see the differences?

  10. +
  11. Can you find a way to compare versions between two arbitrary commits in the repository?

  12. +
  13. Try to rename the branch that you created and then browse the network again.

  14. +
  15. Try to create a tag for one of the commits that you created (on GitHub, +create a “release”).

  16. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+

Solution and walk-through

+
+

(1) Create a new branch and a new commit

+
+
    +
  1. Where it says “main” at the top left, click, enter a new branch +name new-tutorial, click on the offer to create the new branch +(“Create branch new-tutorial from main”).

  2. +
  3. Make sure you are still on the new-tutorial branch (it should say +it at the top), and click “Add file” → “Create new file” from the +upper right.

  4. +
  5. Enter a filename where it says “Name your file…”.

  6. +
  7. Share some Git or programming trick you like.

  8. +
  9. Click “Commit changes”

  10. +
  11. Enter a commit message. Then click “Commit +changes”.

  12. +
+

You should appear back at the file browser view, and see your +modification there.

+
+
+
+

(2) Modify the file again with a new commit

+
+

This is similar to before, but we click on the existing file to +modify.

+
    +
  1. Click on the file you added or modified previously.

  2. +
  3. Click the edit button, the pencil icon at top-right.

  4. +
  5. Follow the “Commit changes” instructions as in the previous step.

  6. +
+
+
+
+

(3) Switch to the main branch and create a commit there

+
+
    +
  1. Go back to the main repository page (your user’s page).

  2. +
  3. In the branch switch view (top left above the file view), switch to +main.

  4. +
  5. Modify another file that already exists, following the pattern +from above.

  6. +
+
+
+
+

(4) Browse the commits you just made

+

Let’s look at what we did. Now, the main and new-tutorial branches +have diverged: both have some modifications. Try to find the commits +you created.

+
+

Insights tab → Network view (just like we have done before).

+
+
+
+

(5) Compare the branches

+

Comparing changes is an important thing we need to do. When using the +GitHub view only, this may not be so common, but we’ll show it so that +it makes sense later on.

+
+

A nice way to compare branches is to add /compare to the URL of the repository, +for example (replace USER): +https://github.com/USER/planets/compare

+
+
+
+

(6) Compare two arbitrary commits

+

This is similar to above, but not only between branches.

+
+

Following the /compare-trick above, one can compare commits on GitHub by +adjusting the following URL: +https://github.com/USER/planets/compare/VERSION1..VERSION2

+

Replace USER with your username and VERSION1 and VERSION2 with a commit hash or branch name. +Please try it out.

+
+
+
+

(7) Renaming a branch

+
+

Branch button → View all branches → three dots at right side → Rename branch.

+
+
+
+

(8) Creating a tag

+

Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

+
+

On the right side, below “Releases”, click on “Create a new release”.

+

What GitHub calls releases are actually tags in Git with additional metadata. +For the purpose of this exercise we can use them interchangeably.

+
+
+
+
+

Summary

+

In this part, we saw how we can make changes to our files. With branches, we +can track several lines of work at once, and can compare their differences.

+
    +
  • You could commit directly to main if there is only one single line +of work and it’s only you.

  • +
  • You could commit to branches if there are multiple lines of work at +once, and you don’t want them to interfere with each other.

  • +
  • Tags are useful to mark a specific commit as important, for example a +release version.

  • +
  • In Git, commits form a so-called “graph”. Branches are tags in Git function +like sticky notes that stick to specific commits. What this means for us is +that it does not cost any significant disk space to create new branches.

  • +
  • Not all files should be added to Git. For example, temporary files or +files with sensitive information or files which are generated as part of +the build process should not be added to Git. For this we use +.gitignore (more about this later: Practical advice: How much Git is necessary?).

  • +
  • Unsure on which branch you are or what state the repository is in? +On the command line, use git status frequently to get a quick overview.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/version-control/browsing/index.html b/branch/main/version-control/browsing/index.html new file mode 100644 index 0000000..b36d1e3 --- /dev/null +++ b/branch/main/version-control/browsing/index.html @@ -0,0 +1,456 @@ + + + + + + + Forking, cloning, and browsing — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Forking, cloning, and browsing

+

In this episode, we will look at an existing repository to understand how +all the pieces work together. Along the way, we will make a copy (by +forking and/or cloning) of the repository for us, which will be used +for our own changes.

+
+

Objectives

+
    +
  • See a real Git repository and understand what is inside of it.

  • +
  • Understand how version control allows advanced inspection of a +repository.

  • +
  • See how Git allows multiple people to work on the same project at the same time.

  • +
  • See the big picture instead of remembering a bunch of commands.

  • +
+
+
+

GitHub, VS Code, or command line

+

We offer three different paths for this exercise:

+
    +
  • GitHub (this is the one we will demonstrate)

  • +
  • VS Code (if you prefer to follow along using an editor)

  • +
  • Command line (for people comfortable with the command line)

  • +
+
+
+

Creating a copy of the repository by “forking” or “cloning”

+

A repository is a collection of files in one directory tracked by Git. A +GitHub repository is GitHub’s copy, which adds things like access control, +issue tracking, and discussions. Each GitHub repository is owned by a user or +organization, who controls access.

+

First, we need to make our own copy of the exercise repository. This will +become important later, when we make our own changes.

+
+Illustration of forking a repository on GitHub +
+

Illustration of forking a repository on GitHub.

+
+
+
+Illustration of cloning a repository to your computer +
+

Illustration of cloning a repository to your computer.

+
+
+
+Illustration of cloning a forked repository to your computer +
+

It is also possible to do this: to clone a forked repository to your computer.

+
+
+

At all times you should be aware of if you are looking at your repository +or the upstream repository (original repository):

+
    +
  • Your repository: https://github.com/USER/planets

  • +
  • Upstream repository: https://github.com/workshop-material/planets

  • +
+
+

How to create a fork

+
    +
  1. Go to the repository view on GitHub: https://github.com/workshop-material/planets

  2. +
  3. First, on GitHub, click the button that says “Fork”. It is towards +the top-right of the screen.

  4. +
  5. You should shortly be redirected to your copy of the repository +USER/planets.

  6. +
+
+
+

Instructor note

+

Before starting the exercise session show +how to fork the repository to own account +(above).

+
+
+
+

Exercise: Copy and browse an existing project

+

Work on this by yourself or in pairs.

+
+

Exercise preparation

+
+

In this case you will work on a fork.

+

You only need to open your own view, as described above. The browser +URL should look like https://github.com/USER/planets, where +USER is your GitHub username.

+
+
+
+

Exercise: Browsing an existing project (20 min)

+

Browse the example project and +explore commits and branches, either on a fork or on a clone. Take notes and +prepare questions. The hints are for the GitHub path in the browser.

+
    +
  1. Browse the commit history: Are commit messages understandable? +(Hint: “Commit history”, the timeline symbol, above the file list)

  2. +
  3. Compare the commit history with the network graph (“Insights” -> “Network”). Can you find the branches?

  4. +
  5. Try to find the history of commits for a single file, e.g. simulate.py. +(Hint: “History” button in the file view)

  6. +
  7. Which files include the word “position”? +(Hint: the GitHub search on top of the repository view)

  8. +
  9. In the simulate.py file, find out who modified each line last and when +(click on file, then click “Blame” button). Find out who modified the gravitational constant +last and in which commit. +(Hint: “Blame” view in the file view)

  10. +
  11. Can you use this code yourself? Are you allowed to share +modifications? +(Hint: look for a license file)

  12. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+

Solution and walk-through

+
+

(1) Basic browsing

+

The most basic thing to look at is the history of commits.

+
    +
  • This is visible from a button in the repository view. We see every +change, when, and who has committed.

  • +
  • Every change has a unique identifier, such as 244c993. This can +be used to identify both this change, and the whole project’s +version as of that change.

  • +
  • Clicking on a change in the view shows more.

  • +
+
+

Click on the timeline symbol in the repository view:

+
+Screenshot on GitHub of where to find the commit history +
+
+
+
+

(2) Compare commit history with network graph

+

The commit history we saw above looks linear: one commit after +another. But if we look at the network view, we see some branches and +merges. We’ll see how to do these later. This is another one of the +basic Git views.

+
+

In a new browser tab, open the “Insights” tab, and click on “Network”. +You can hover over the commit dots to see the person who committed and +how they correspond with the commits in the other view:

+
+Screenshot on GitHub of the network graph +
+
+
+
+

(3) How can you browse the history of a single file?

+

We see the history for the whole repository, but we can also see it +for a single file.

+
+

Navigate to the file view: Main page → simulate.py. +Click the “History” button near the top right.

+
+
+
+

(4) Which files include the word “position”?

+

Version control makes it very easy to find all occurrences of a single +word. This is useful for things like finding where functions or +variables are defined or used.

+
+

We go to the main file view. We click the Search magnifying +class at the very top, type “position”, and click enter. We see every +instance, including the context.

+
+

Searching in a forked repository will not work instantaneously!

+

It usually takes a few minutes before one can search for keywords in a forked repository +since it first needs to build the search index the very first time we search. +Start it, continue with other steps, then come back to this.

+
+
+
+
+

(5) Who modified each line last and when?

+

This is called the “annotate” or “blame” view. The name “blame” +is very unfortunate, but it is the standard term for historical reasons +for this functionality and it is not meant to blame anyone.

+
+

From a file view, change preview to “Blame” towards the top-left. +To get the actual commit, click on the commit message.

+
+
+
+

(6) Can you use this code yourself? Are you allowed to share modifications?

+
    +
  • Look at the file LICENSE.

  • +
  • On GitHub, click on the file to see a nice summary of what we can do with this:

    +
    +Screenshot on GitHub summarizing license terms +
    +
  • +
+
+
+

Summary

+
    +
  • Git allowed us to understand this simple project much better than we +could, if it was just a few files on our own computer.

  • +
  • It was easy to share the project with the course.

  • +
  • By forking the repository, we created our own copy. This is +important for the following, where we will make changes to +our copy.

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/version-control/index.html b/branch/main/version-control/index.html new file mode 100644 index 0000000..f5e96bc --- /dev/null +++ b/branch/main/version-control/index.html @@ -0,0 +1,167 @@ + + + + + + + Introduction to version control with Git and GitHub — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/branch/main/version-control/motivation/index.html b/branch/main/version-control/motivation/index.html new file mode 100644 index 0000000..454a972 --- /dev/null +++ b/branch/main/version-control/motivation/index.html @@ -0,0 +1,283 @@ + + + + + + + Motivation — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+ +
+
+ +
+

Motivation

+
+

Objectives

+
    +
  • Browse commits and branches of a Git repository.

  • +
  • Remember that commits are like snapshots of the repository at a certain +point in time.

  • +
  • Know the difference between Git (something that tracks changes) and +GitHub/GitLab (a platform to host Git repositories).

  • +
+
+
+

Why do we need to keep track of versions?

+

Version control is an answer to the following questions (do you recognize some +of them?):

+
    +
  • “It broke … hopefully I have a working version somewhere?”

  • +
  • “Can you please send me the latest version?”

  • +
  • “Where is the latest version?”

  • +
  • “Which version are you using?”

  • +
  • “Which version have the authors used in the paper I am trying to reproduce?”

  • +
  • “Found a bug! Since when was it there?”

  • +
  • “I am sure it used to work. When did it change?”

  • +
  • “My laptop is gone. Is my thesis now gone?”

  • +
+
+
+

Demonstration

+
    +
  • Example repository: https://github.com/workshop-material/planets

  • +
  • Commits are like snapshots and if we break something we can go back to a +previous snapshot.

  • +
  • Commits carry metadata about changes: author, date, commit message, and +a checksum.

  • +
  • Branches are like parallel universes where you can experiment with +changes without affecting the default branch: +https://github.com/workshop-material/planets/network +(“Insights” -> “Network”)

  • +
  • With version control we can annotate code +(example).

  • +
  • Collaboration: We can fork (make a copy on GitHub), clone (make a copy +to our computer), review, compare, share, and discuss.

  • +
  • Code review: Others can suggest changes using pull requests or merge +requests. These can be reviewed and discussed before they are merged. +Conceptually, they are similar to “suggesting changes” in Google Docs.

  • +
+
+
+

Features: roll-back, branching, merging, collaboration

+
    +
  • Roll-back: you can always go back to a previous version and compare

  • +
  • Branching and merging:

    +
      +
    • Work on different ideas at the same time

    • +
    • Different people can work on the same code/project without interfering

    • +
    • You can experiment with an idea and discard it if it turns out to be a bad idea

    • +
    +
  • +
+
+Branching explained with a gopher +
+

Image created using https://gopherize.me/ +(inspiration).

+
+
+ +
+
+

Talking about code

+

Which of these two is more practical?

+
    +
  1. “Clone the code, go to the file ‘simulate.py’, and search for ‘force_between_planets’. +Oh! But make sure you use the version from September 2024.”

  2. +
  3. Or I can send you a permalink: https://github.com/workshop-material/planets/blob/1343ac0/simulate.py#L31C5-L39

  4. +
+
+
+

What we typically like to snapshot

+
    +
  • Software (this is how it started but Git/GitHub can track a lot more)

  • +
  • Scripts

  • +
  • Documents (plain text files much better suitable than Word documents)

  • +
  • Manuscripts (Git is great for collaborating/sharing LaTeX or Quarto manuscripts)

  • +
  • Configuration files

  • +
  • Website sources

  • +
  • Data

  • +
+
+

Discussion

+

In this example somebody tried to keep track of versions without a version +control system tool like Git. Discuss the following directory listing. What +possible problems do you anticipate with this kind of “version control”:

+
myproject-2019.zip
+myproject-2020-february.zip
+myproject-2021-august.zip
+myproject-2023-09-19-working.zip
+myproject-2023-09-21.zip
+myproject-2023-09-21-test.zip
+myproject-2023-09-21-myversion.zip
+myproject-2023-09-21-newfeature.zip
+...
+(100 more files like these)
+
+
+ +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/version-control/practical-advice/index.html b/branch/main/version-control/practical-advice/index.html new file mode 100644 index 0000000..bcc49ec --- /dev/null +++ b/branch/main/version-control/practical-advice/index.html @@ -0,0 +1,300 @@ + + + + + + + Practical advice: How much Git is necessary? — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Practical advice: How much Git is necessary?

+
+

Writing useful commit messages

+

Useful commit messages summarize the change and provide context.

+

If you need a commit message that is longer than one line, +then the convention is: one line summarizing the commit, then one empty line, +then paragraph(s) with more details in free form, if necessary.

+

Good example:

+
increase alpha to 2.0 for faster convergence
+
+the motivation for this change is
+to enable ...
+...
+(more context)
+...
+this is based on a discussion in #123
+
+
+
    +
  • Why something was changed is more important than what has changed.

  • +
  • Cross-reference to issues and discussions if possible/relevant.

  • +
  • Bad commit messages: “fix”, “oops”, “save work”

  • +
  • Bad examples: http://whatthecommit.com

  • +
  • Write commit messages that will be understood +15 years from now by someone else than you. Or by your future you.

  • +
  • Many projects start out as projects “just for me” and end up to be successful projects +that are developed by 50 people over decades.

  • +
  • Commits with multiple authors are possible.

  • +
+

Good references:

+ +
+

Note

+

A great way to learn how to write commit messages and to get inspired by their +style choices: browse repositories of codes that you use/like:

+

Some examples (but there are so many good examples):

+ +

When designing commit message styles consider also these:

+
    +
  • How will you easily generate a changelog or release notes?

  • +
  • During code review, you can help each other improving commit messages.

  • +
+
+

But remember: it is better to make any commit, than no commit. Especially in small projects. +Let not the perfect be the enemy of the good enough.

+
+
+

What level of branching complexity is necessary for each project?

+

Simple personal projects:

+
    +
  • Typically start with just the main branch.

  • +
  • Use branches for unfinished/untested ideas.

  • +
  • Use branches when you are not sure about a change.

  • +
  • Use tags to mark important milestones.

  • +
  • If you are unsure what to do with unfinished and not working code, commit it +to a branch.

  • +
+

Projects with few persons: you accept things breaking sometimes

+
    +
  • It might be reasonable to commit to the main branch and feature branches.

  • +
+

Projects with few persons: changes are reviewed by others

+
    +
  • You create new feature branches for changes.

  • +
  • Changes are reviewed before they are merged to the main branch.

  • +
  • Consider to write-protect the main branch so that it can only be changed +with pull requests or merge requests.

  • +
+
+
+

How large should a commit be?

+
    +
  • Better too small than too large (easier to combine than to split).

  • +
  • Often I make a commit at the end of the day (this is a unit I would not like to lose).

  • +
  • Smaller sized commits may be easier to review for others than huge commits.

  • +
  • Imperfect commits are better than no commits.

  • +
  • A commit should not contain unrelated changes to simplify review and possible +repair/adjustments/undo later (but again: imperfect commits are better than no commits).

  • +
+
+
+

Working on the command line? Use “git status” all the time

+

The git status command is one of the most useful commands in Git +to inform about which branch we are on, what we are about to commit, +which files might not be tracked, etc.

+
+
+

How about staging and committing?

+
    +
  • Commit early and often: rather create too many commits than too few. +You can always combine commits later.

  • +
  • Once you commit, it is very, very hard to really lose your code.

  • +
  • Always fully commit (or stash) before you do dangerous things, so that you know you are safe. +Otherwise it can be hard to recover.

  • +
  • Later you can start using the staging area (where you first stage and then commit in a second step).

  • +
  • Later start using git add -p and/or git commit -p.

  • +
+
+
+

What to avoid

+
    +
  • Committing generated files/directories (example: __pycache__, *.pyc) -> +use .gitignore +files +(collection of .gitignore templates).

  • +
  • Committing huge files -> use code review to detect this.

  • +
  • Committing unrelated changes together.

  • +
  • Postponing commits because the changes are “unfinished”/”ugly” -> better ugly +commits than no commits.

  • +
  • When working with branches:

    +
      +
    • Not updating your branch before starting new work.

    • +
    • Too ambitious branch which risks to never get completed.

    • +
    • Over-engineering the branch layout and safeguards in small projects.

    • +
    • Working on unrelated things on the same branch.

    • +
    +
  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/branch/main/version-control/sharing/index.html b/branch/main/version-control/sharing/index.html new file mode 100644 index 0000000..7621f54 --- /dev/null +++ b/branch/main/version-control/sharing/index.html @@ -0,0 +1,405 @@ + + + + + + + Optional: How to turn your project to a Git repo and share it — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Optional: How to turn your project to a Git repo and share it

+
+

Objectives

+
    +
  • Turn our own coding project (small or large, finished or unfinished) into a +Git repository.

  • +
  • Be able to share a repository on the web to have a backup or so that others +can reuse and collaborate or even just find it.

  • +
+
+
+

Exercise

+
+Illustration of how we turn a project into a Git repository and share it +
+

From a bunch of files to a local repository which we then share on GitHub.

+
+
+
+

Exercise: Turn your project to a Git repo and share it (20 min)

+
    +
  1. Create a new directory called myproject with one or few files in it. +This represents our own project. It is not yet a Git repository. You can try +that with your own project or use a simple placeholder example.

  2. +
  3. Turn this new directory into a Git repository.

  4. +
  5. Share this repository on GitHub (or GitLab, since it really works the same).

  6. +
+

We offer three different paths of how to do this exercise.

+
    +
  • Via GitHub web interface: easy and can be a good starting point if you are completely +new to Git.

  • +
  • VS Code is quite easy, since VS Code can offer to create the +GitHub repositories for you.

  • +
  • Command line: you need to create the +repository on GitHub and link it yourself.

  • +
+
+

Create an repository on GitHub

+

First log into GitHub, then follow the screenshots and descriptions below.

+
+Screenshot on GitHub before a new repository form is opened +
+

Click on the “plus” symbol on top right, then on “New repository”.

+
+
+

Then:

+
+Screenshot on GitHub just before a new repository is created +
+

Choose a repository name, add a short description, and in this case make sure to check “Add a +README file”. Finally “Create repository”.

+
+
+

Upload your files

+

Now that the repository is created, you can upload your files:

+
+Screenshot on GitHub just before uploading files +
+

Click on the “+” symbol and then on “Upload files”.

+
+
+
+
+
+
+

Is putting software on GitHub/GitLab/… publishing?

+

It is a good first step but to make your code truly findable and +accessible, consider making your code citable and persistent: Get a +persistent identifier (PID) such as DOI in addition to sharing the code +publicly, by using services like Zenodo or similar +services.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/collaboration/code-review/index.html b/collaboration/code-review/index.html new file mode 100644 index 0000000..2db66b2 --- /dev/null +++ b/collaboration/code-review/index.html @@ -0,0 +1,289 @@ + + + + + + + Practicing code review — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Practicing code review

+

In this episode we will practice the code review process. We will learn how to +ask for changes in a pull request, how to suggest a change in a pull request, +and how to modify a pull request.

+

This will enable research groups to work more collaboratively and to not only +improve the code quality but also to learn from each other.

+
+

Exercise

+
+

Exercise preparation

+

We can continue in the same exercise repository which we have used in the +previous episode.

+
+
+

Exercise: Practicing code review (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • As a reviewer, we will learn how to ask for changes in a pull request.

  • +
  • As a reviewer, we will learn how to suggest a change in a pull request.

  • +
  • As a submitter, we will learn how to modify a pull request without closing +the incomplete one and opening a new one.

  • +
+

Exercise tasks:

+
    +
  1. Create a new branch and one or few commits: in these improve something but also +deliberately introduce a typo and also a larger mistake which we will want to fix during the code review.

  2. +
  3. Open a pull request towards the main branch.

  4. +
  5. As a reviewer to somebody else’s pull request, ask for an improvement and +also directly suggest a change for the small typo. (Hint: +suggestions are possible through the GitHub web interface, view of +a pull request, “Files changed” view, after selecting some lines. +Look for the “±” button.)

  6. +
  7. As the submitter, learn how to accept the suggested change. (Hint: +GitHub web interface, “Files Changed” view.)

  8. +
  9. As the submitter, improve the pull request without having to close and open +a new one: by adding a new commit to the same branch. (Hint: push +to the branch again.)

  10. +
  11. Once the changes are addressed, merge the pull request.

  12. +
+
+
+
+

Help and discussion

+

From here on out, we don’t give detailed steps to the solution. You +need to combine what you know, and the extra info below, in order to +solve the above.

+
+

How to ask for changes in a pull request

+

Technically, there are at least two common ways to ask for changes in a pull +request.

+

Either in the comment field of the pull request:

+
+Screenshot of a pull request comment field +
+

Or by using the “Review changes”:

+
+Screenshot of a pull request navigating to the "Review changes" tab +
+

And always please be kind and constructive in your comments. Remember that the +goal is not gate-keeping but collaborative learning.

+
+
+

How to suggest a change in a pull request as a reviewer

+

If you see a very small problem that is easy to fix, you can suggest a change +as a reviewer.

+

Instead of asking the submitter to tiny problem, you can suggest a change by +clicking on the plus sign next to the line number in the “Files changed” tab:

+
+Screenshot of leaving a comment to a line in a pull request +
+

Here you can comment on specific lines or even line ranges.

+

But now the interesting part is to click on the “Add a suggestion” symbol (the +one that looks like plus and minus). Now you can fix the tiny problem (in this +case a typo) and then click on the “Add single comment” button:

+
+Sequence of clicks to add a suggestion to a line in a pull request +
+

The result is this and the submitter can accept the change with a single click:

+
+Screenshot of a pull request with a suggested change +
+

After accepting with “Commit suggestion”, the improvement gets added to the +pull request.

+
+
+

How to modify a pull request to address the review comments

+

If the reviewer asks for changes, it is not necessary to close the pull request +and later open a new one. It can even be counter-productive to do so: This can +fragment the discussion and the history of the pull request and can make it +harder to understand the context of the changes.

+

A much better mechanism to recognize that pull requests are not implemented +from a specific commit to a specific branch, but always from a branch to a +branch.

+

This means that you can make amendments to the pull request by adding new +commits to the same source branch. This way the pull request will be updated +automatically and the reviewer can see the new changes and comment on them.

+

The fact that pull requests are from branch to branch also strongly suggests +that it is a good practice to create a new branch for each pull request. +Otherwise you could accidentally modify an open pull request by adding new +commits to the source branch.

+
+
+
+

Summary

+
    +
  • Our process isn’t just about code now. It’s about discussion and +working together to make the whole process better.

  • +
  • GitHub discussions and reviewing are quite powerful and can make +small changes easy.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/collaboration/concepts/index.html b/collaboration/concepts/index.html new file mode 100644 index 0000000..f4c073d --- /dev/null +++ b/collaboration/concepts/index.html @@ -0,0 +1,234 @@ + + + + + + + Concepts around collaboration — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Concepts around collaboration

+
+

Objectives

+
    +
  • Be able to decide whether to divide work at the branch level or at the repository level.

  • +
+
+
+

Commits, branches, repositories, forks, clones

+
    +
  • repository: The project, contains all data and history (commits, branches, tags).

  • +
  • commit: Snapshot of the project, gets a unique identifier (e.g. c7f0e8bfc718be04525847fc7ac237f470add76e).

  • +
  • branch: Independent development line. The main development line is often called main.

  • +
  • tag: A pointer to one commit, to be able to refer to it later. Like a commemorative plaque +that you attach to a particular commit (e.g. phd-printed or paper-submitted).

  • +
  • cloning: Copying the whole repository to your laptop - the first time. It is not necessary to download each file one by one.

  • +
  • forking: Taking a copy of a repository (which is typically not yours) - your +copy (fork) stays on GitHub/GitLab and you can make changes to your copy.

  • +
+
+
+

Cloning a repository

+

In order to make a complete copy a whole repository, the git clone command +can be used. When cloning, all the files, of all or selected branches, of a +repository are copied in one operation. Cloning of a repository is of relevance +in a few different situations:

+
    +
  • Working on your own, cloning is the operation that you can use to create +multiple instances of a repository on, for instance, a personal computer, a +server, and a supercomputer.

  • +
  • The parent repository could be a repository that you or your colleague own. A +common use case for cloning is when working together within a smaller team +where everyone has read and write access to the same git repository.

  • +
  • Alternatively, cloning can be made from a public repository of a code that +you would like to use. Perhaps you have no intention to work on the code, but +would like to stay in tune with the latest developments, also in-between +releases of new versions of the code.

  • +
+
+Forking and cloning +
+

Forking and cloning

+
+
+
+
+

Forking a repository

+

When a fork is made on GitHub/GitLab a complete copy, of all or selected +branches, of the repository is made. The copy will reside under a different +account on GitHub/GitLab. Forking of a repository is of high relevance when +working with a git repository to which you do not have write access.

+
    +
  • In the fork repository commits can be made to the base branch (main or +master), and to other branches.

  • +
  • The commits that are made within the branches of the fork repository can be +contributed back to the parent repository by means of pull or merge requests.

  • +
+
+
+

Synchronizing changes between repositories

+
    +
  • We need a mechanism to communicate changes between the repositories.

  • +
  • We will pull or fetch updates from remote repositories (we will soon discuss the difference between pull and fetch).

  • +
  • We will push updates to remote repositories.

  • +
  • We will learn how to suggest changes within repositories on GitHub and across repositories (pull request).

  • +
  • Repositories that are forked or cloned do not automatically synchronize themselves: +We will learn how to update forks (by pulling from the “central” repository).

  • +
  • A main difference between cloning a repository and forking a repository is that the former is a general operation for generating copies of a repository to different computers, whereas forking is a particular operation implemented on GitHub/GitLab.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/collaboration/demo-discussion/index.html b/collaboration/demo-discussion/index.html new file mode 100644 index 0000000..405df1b --- /dev/null +++ b/collaboration/demo-discussion/index.html @@ -0,0 +1,160 @@ + + + + + + + Conclict resolution, rebasing, and organizational strategies — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/collaboration/forking-workflow/index.html b/collaboration/forking-workflow/index.html new file mode 100644 index 0000000..ed602bb --- /dev/null +++ b/collaboration/forking-workflow/index.html @@ -0,0 +1,376 @@ + + + + + + + How to contribute changes to repositories that belong to others — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

How to contribute changes to repositories that belong to others

+

In this episode we prepare you to suggest and contribute changes to +repositories that belong to others. These might be open source projects that +you use in your work.

+

We will see how Git and services like GitHub or GitLab can be used to suggest +modification without having to ask for write access to the repository and +accept modifications without having to grant write access to others.

+
+

Exercise

+
+

Exercise preparation

+
    +
  • The exercise repository is now different: +https://github.com/workshop-material/recipe-book-forking-exercise (note the -forking-exercise).

  • +
  • First fork the exercise repository to your GitHub account.

  • +
  • Then clone your fork to your computer (if you wish to work locally).

  • +
  • Double-check that you have forked the correct repository.

  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Forking a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • Opening a pull request towards the upstream repository.

  • +
  • Pull requests can be coupled with automated testing.

  • +
  • Learning that your fork can get out of date.

  • +
  • After the pull requests are merged, updating your fork with the changes.

  • +
  • Learn how to approach other people’s repositories with ideas, changes, and requests.

  • +
+

Exercise tasks:

+
    +
  1. Open an issue in the upstream exercise repository where you describe the +change you want to make. Take note of the issue number.

  2. +
  3. Create a new branch in your fork of the repository.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit cross-reference the issue you opened. +See the walk-through below for how to do this.

  6. +
  7. Open a pull request towards the upstream repository.

  8. +
  9. The instructor will review and merge the pull requests. +During the review, pay attention to the automated test step (here for +demonstration purposes, we test whether the recipe contains an ingredients +and an instructions sections).

  10. +
  11. After few pull requests are merged, update your fork with the changes.

  12. +
  13. Check that in your fork you can see changes from other people’s pull requests.

  14. +
+
+
+
+

Help and discussion

+
+

Help! I don’t have permissions to push my local changes

+

Maybe you see an error like this one:

+
Please make sure you have the correct access rights
+and the repository exists.
+
+
+

Or like this one:

+
failed to push some refs to workshop-material/recipe-book-forking-exercise.git
+
+
+

In this case you probably try to push the changes not to your fork but to the original repository +and in this exercise you do not have write access to the original repository.

+

The simpler solution is to clone again but this time your fork.

+ +
+
+

Opening a pull request towards the upstream repository

+

We have learned in the previous episode that pull requests are always from +branch to branch. But the branch can be in a different repository.

+

When you open a pull request in a fork, by default GitHub will suggest to +direct it towards the default branch of the upstream repository.

+

This can be changed and it should always be verified, but in this case this is +exactly what we want to do, from fork towards upstream:

+
+Screenshot of a pull request from fork towards upstream +
+
+
+

Pull requests can be coupled with automated testing

+

We added an automated test here just for fun and so that you see that this is +possible to do.

+

In this exercise, the test is silly. It will check whether the recipe contains +both an ingredients and an instructions section.

+

In this example the test failed:

+
+Screenshot of a failed test in a pull request +
+

Click on the “Details” link to see the details of the failed test:

+
+Screenshot of details why the test failed +
+

How can this be useful?

+
    +
  • The project can define what kind of tests are expected to pass before a pull +request can be merged.

  • +
  • The reviewer can see the results of the tests, without having to run them +locally.

  • +
+

How does it work?

+
    +
  • We added a GitHub Actions workflow +to automatically run on each push or pull request towards the main branch.

  • +
+

What tests or steps can you image for your project to run automatically with +each pull request?

+
+
+

How to update your fork with changes from upstream

+

This used to be difficult but now it is two mouse clicks.

+

Navigate to your fork and notice how GitHub tells you that your fork is behind. +In my case, it is 9 commits behind upstream. To fix this, click on “Sync fork” +and then “Update branch”:

+
+Screenshot on GitHub fork page showing that the fork is behind +
+

After the update my “branch is up to date” with the upstream repository:

+
+Screenshot on GitHub after fork has been updated +
+
+
+

How to approach other people’s repositories with ideas, changes, and requests

+

Contributing very minor changes

+
    +
  • Clone or fork+clone repository

  • +
  • Create a branch

  • +
  • Commit and push change

  • +
  • Open a pull request or merge request

  • +
+

If you observe an issue and have an idea how to fix it

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • Describe the problem

  • +
  • If you have a suggestion on how to fix it, describe your suggestion

  • +
  • Possibly discuss and get feedback

  • +
  • If you are working on the fix, indicate it in the issue so that others know that somebody is working on it and who is working on it

  • +
  • Submit your fix as pull request or merge request which references/closes the issue

  • +
+
+

Motivation

+
    +
  • Inform others about an observed problem

  • +
  • Make it clear whether this issue is up for grabs or already being worked on

  • +
+
+

If you have an idea for a new feature

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • In the issue, write a short proposal for your suggested change or new feature

  • +
  • Motivate why and how you wish to do this

  • +
  • Also indicate where you are unsure and where you would like feedback

  • +
  • Discuss and get feedback before you code

  • +
  • Once you start coding, indicate that you are working on it

  • +
  • Once you are done, submit your new feature as pull request or merge request which references/closes the issue/proposal

  • +
+
+

Motivation

+
    +
  • Get agreement and feedback before writing 5000 lines of code which might be rejected

  • +
  • If we later wonder why something was done, we have the issue/proposal as +reference and can read up on the reasoning behind a code change

  • +
+
+
+
+
+

Summary

+
    +
  • This forking workflow lets you propose changes to repositories for +which you have no write access.

  • +
  • This is the way that much modern open-source software works.

  • +
  • You can now contribute to any project you can view.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/collaboration/index.html b/collaboration/index.html new file mode 100644 index 0000000..975bbbe --- /dev/null +++ b/collaboration/index.html @@ -0,0 +1,167 @@ + + + + + + + Collaborative version control and code review — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/collaboration/same-repository/index.html b/collaboration/same-repository/index.html new file mode 100644 index 0000000..e901473 --- /dev/null +++ b/collaboration/same-repository/index.html @@ -0,0 +1,423 @@ + + + + + + + Collaborating within the same repository — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Collaborating within the same repository

+

In this episode, we will learn how to collaborate within the same repository. +We will learn how to cross-reference issues and pull requests, how to review +pull requests, and how to use draft pull requests.

+

This exercise will form a good basis for collaboration that is suitable for +most research groups.

+
+

Note

+

When you read or hear pull request, please think of a change proposal.

+
+
+

Exercise

+

In this exercise, we will contribute to a repository via a pull request. +This means that you propose some change, and then it is accepted (or not).

+
+

Exercise preparation

+
    +
  • First we need to get access to the exercise repository to which we will +contribute.

    +
      +
    • Instructor collects GitHub usernames from learners and adds them as collaborators to the exercise repository +(Settings -> Collaborators and teams -> Manage access -> Add people).

    • +
    +
  • +
  • Don’t forget to accept the invitation

    +
      +
    • Check https://github.com/settings/organizations/

    • +
    • Alternatively check the inbox for the email account you registered with +GitHub. GitHub emails you an invitation link, but if you don’t receive it +you can go to your GitHub notifications in the top right corner. The +maintainer can also “copy invite link” and share it within the group.

    • +
    +
  • +
  • Watching and unwatching repositories

    +
      +
    • Now that you are a collaborator, you get notified about new issues and pull +requests via email.

    • +
    • If you do not wish this, you can “unwatch” a repository (top of +the project page).

    • +
    • However, we recommend watching repositories you are interested +in. You can learn things from experts just by watching the +activity that come through a popular project.

    • +
    +
    +Unwatching a repository +
    +

    Unwatch a repository by clicking “Unwatch” in the repository view, +then “Participating and @mentions” - this way, you will get +notifications about your own interactions.

    +
    +
    +
  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements (from installation instructions):

+ +

What is familiar from the previous workshop day (not repeated here):

+
    +
  • Cloning a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Submit a pull request towards the main branch.

  • +
+

What will be new in this exercise:

+
    +
  • If you create the changes locally, you will need to push them to the remote repository.

  • +
  • Learning what a protected branch is and how to modify a protected branch: using a pull request.

  • +
  • Cross-referencing issues and pull requests.

  • +
  • Practice to review a pull request.

  • +
  • Learn about the value of draft pull requests.

  • +
+

Exercise tasks:

+
    +
  1. Start in the exercise +repository and open an +issue where you describe the change you want to make. Note down the issue +number since you will need it later.

  2. +
  3. Create a new branch.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit +cross-reference the issue you opened (see the walk-through below +for how to do that).

  6. +
  7. Push your new branch (with the new commit) to the repository you +are working on.

  8. +
  9. Open a pull request towards the main branch.

  10. +
  11. Review somebody else’s pull request and give constructive feedback. Merge their pull request.

  12. +
  13. Try to create a new branch with some half-finished work and open a draft +pull request. Verify that the draft pull request cannot be merged since it +is not meant to be merged yet.

  14. +
+
+
+
+

Solution and hints

+
+

(1) Opening an issue

+

This is done through the GitHub web interface. For example, you could +give the name of the recipe you want to add (so that others don’t add +the same one). It is the “Issues” tab.

+
+
+

(2) Create a new branch.

+

If on GitHub, you can make the branch in the web interface.

+
+
+

(3) Make a change adding the recipe

+

Add a new file with the recipe in it. Commit the file. In the commit +message, include the note about the issue number, saying that this +will close that issue.

+
+

Cross-referencing issues and pull requests

+

Each issue and each pull request gets a number and you can cross-reference them.

+

When you open an issue, note down the issue number (in this case it is #2):

+
+Each issue gets a number +
+

You can reference this issue number in a commit message or in a pull request, like in this +commit message:

+
this is the new recipe; fixes #2
+
+
+

If you forget to do that in your commit message, you can also reference the issue +in the pull request description. And instead of fixes you can also use closes or resolves +or fix or close or resolve (case insensitive).

+

Here are all the keywords that GitHub recognizes: +https://help.github.com/en/articles/closing-issues-using-keywords

+

Then observe what happens in the issue once your commit gets merged: it will +automatically close the issue and create a link between the issue and the +commit. This is very useful for tracking what changes were made in response to +which issue and to know from when until when precisely the issue was open.

+
+
+
+

(4) Push to GitHub as a new branch

+

Push the branch to the repository. You should end up with a branch +visible in the GitHub web view.

+

This is only necessary if you created the changes locally. If you created the +changes directly on GitHub, you can skip this step.

+
+

In VS Code, you can “publish the branch” to the remote repository by clicking +the cloud icon in the bottom left corner of the window:

+
+Publishing a branch in VS Code +
+
+
+
+

(5) Open a pull request towards the main branch

+

This is done through the GitHub web interface.

+
+
+

(6) Reviewing pull requests

+

You review through the GitHub web interface.

+

Checklist for reviewing a pull request:

+
    +
  • Be kind, on the other side is a human who has put effort into this.

  • +
  • Be constructive: if you see a problem, suggest a solution.

  • +
  • Towards which branch is this directed?

  • +
  • Is the title descriptive?

  • +
  • Is the description informative?

  • +
  • Scroll down to see commits.

  • +
  • Scroll down to see the changes.

  • +
  • If you get incredibly many changes, also consider the license or copyright +and ask where all that code is coming from.

  • +
  • Again, be kind and constructive.

  • +
  • Later we will learn how to suggest changes directly in the pull request.

  • +
+

If someone is new, it’s often nice to say something encouraging in the +comments before merging (even if it’s just “thanks”). If all is good +and there’s not much else to say, you could merge directly.

+
+
+

(7) Draft pull requests

+

Try to create a draft pull request:

+
+Creating a draft pull request +
+

Verify that the draft pull request cannot be merged until it is marked as ready +for review:

+
+Draft pull request cannot be merged +
+

Draft pull requests can be useful for:

+
    +
  • Feedback: You can open a pull request early to get feedback on your work without +signaling that it is ready to merge.

  • +
  • Information: They can help communicating to others that a change is coming up and in +progress.

  • +
+
+
+

What is a protected branch? And how to modify it?

+

A protected branch on GitHub or GitLab is a branch that cannot (accidentally) +deleted or force-pushed to. It is also possible to require that a branch cannot +be directly pushed to or modified, but that changes must be submitted via a +pull request.

+

To protect a branch in your own repository, go to “Settings” -> “Branches”.

+
+
+

Summary

+
    +
  • We used all the same pieces that we’ve learned previously.

  • +
  • But we successfully contributed to a collaborative project!

  • +
  • The pull request allowed us to contribute without changing directly: +this is very good when it’s not mainly our project.

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/credit/index.html b/credit/index.html new file mode 100644 index 0000000..e4d14e5 --- /dev/null +++ b/credit/index.html @@ -0,0 +1,158 @@ + + + + + + + Credit — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/documentation/index.html b/documentation/index.html new file mode 100644 index 0000000..74b8b75 --- /dev/null +++ b/documentation/index.html @@ -0,0 +1,540 @@ + + + + + + + Code documentation — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Code documentation

+
+

Objectives

+
    +
  • Discuss what makes good documentation.

  • +
  • Improve the README of your project or our example project.

  • +
  • Explore Sphinx which is a popular tool to build documentation websites.

  • +
  • Learn how to leverage GitHub Actions and GitHub Pages to build and deploy documentation.

  • +
+
+
+

Instructor note

+
    +
  • (30 min) Discussion

  • +
  • (30 min) Exercise: Set up a Sphinx documentation and add API documentation

  • +
  • (15 min) Demo: Building documentation with GitHub Actions

  • +
+
+
+

Why? 💗✉️ to your future self

+
    +
  • You will probably use your code in the future and may forget details.

  • +
  • You may want others to use your code or contribute +(almost impossible without documentation).

  • +
+
+
+

In-code documentation

+

Not very useful (more commentary than comment):

+
# now we check if temperature is below -50
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

More useful (explaining why):

+
# we regard temperatures below -50 degrees as measurement errors
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

Keeping zombie code “just in case” (rather use version control):

+
# do not run this code!
+# if temperature > 0:
+#     print("It is warm")
+
+
+

Emulating version control:

+
# John Doe: threshold changed from 0 to 15 on August 5, 2013
+if temperature > 15:
+    print("It is warm")
+
+
+
+
+

Many languages allow “docstrings”

+

Example (Python):

+
def kelvin_to_celsius(temp_k: float) -> float:
+    """
+    Converts temperature in Kelvin to Celsius.
+
+    Parameters
+    ----------
+    temp_k : float
+        temperature in Kelvin
+
+    Returns
+    -------
+    temp_c : float
+        temperature in Celsius
+    """
+    assert temp_k >= 0.0, "ERROR: negative T_K"
+
+    temp_c = temp_k - 273.15
+
+    return temp_c
+
+
+
+

Keypoints

+
    +
  • Documentation which is only in the source code is not enough.

  • +
  • Often a README is enough.

  • +
  • Documentation needs to be kept +in the same Git repository as the code since we want it to evolve with +the code.

  • +
+
+
+
+

Often a README is enough - checklist

+
    +
  • Purpose

  • +
  • Requirements

  • +
  • Installation instructions

  • +
  • Copy-paste-able example to get started

  • +
  • Tutorials covering key functionality

  • +
  • Reference documentation (e.g. API) covering all functionality

  • +
  • Authors and recommended citation

  • +
  • License

  • +
  • Contribution guide

  • +
+

See also the +JOSS review checklist.

+
+
+

What if you need more than a README?

+ +
+
+

Diátaxis

+

Diátaxis is a systematic approach to technical documentation authoring.

+ +
+
+

Exercise: Set up a Sphinx documentation

+
+

Preparation

+

In this episode we will use the following 5 packages which we installed +previously as part of the Conda environment or Virtual environment:

+
myst-parser
+sphinx
+sphinx-rtd-theme
+sphinx-autoapi
+sphinx-autobuild
+
+
+

Which repository to use? You have 3 options:

+
    +
  • Clone your fork of the planets example repository.

  • +
  • If you don’t have that, you can clone the original repository itself: +https://github.com/workshop-material/planets

  • +
  • You can try this with your own project and the project does not have to +be a Python project.

  • +
+
+

There are at least two ways to get started with Sphinx:

+
    +
  1. Use sphinx-quickstart to create a new Sphinx project.

  2. +
  3. This is what we will do: Create three files (doc/conf.py, doc/index.md, and doc/about.md) +as starting point and improve from there.

  4. +
+
+

Exercise: Set up a Sphinx documentation

+
    +
  1. Create the following three files in your project:

    +
    planets/
    +├── doc/
    +│   ├── conf.py
    +│   ├── index.md
    +│   └── about.md
    +└── ...
    +
    +
    +

    This is conf.py:

    +
    project = "planets"
    +copyright = "2024, Authors"
    +author = "Authors"
    +release = "0.1"
    +
    +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
    +
    +extensions = [
    +    "myst_parser",  # in order to use markdown
    +]
    +
    +myst_enable_extensions = [
    +    "colon_fence",  # ::: can be used instead of ``` for better rendering
    +]
    +
    +html_theme = "sphinx_rtd_theme"
    +
    +
    +

    This is index.md (feel free to adjust):

    +
    # Our code documentation
    +
    +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
    +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
    +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
    +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
    +culpa qui officia deserunt mollit anim id est laborum.
    +
    +:::{toctree}
    +:maxdepth: 2
    +:caption: Some caption
    +
    +about.md
    +:::
    +
    +
    +

    This is about.md (feel free to adjust):

    +
    # About this code
    +
    +Work in progress ...
    +
    +
    +
  2. +
  3. Run sphinx-build to build the HTML documentation:

    +
    $ sphinx-build doc _build
    +
    +... lots of output ...
    +The HTML pages are in _build.
    +
    +
    +
  4. +
  5. Try to open _build/index.html in your browser.

  6. +
  7. Experiment with adding more content, images, equations, code blocks, …

    + +
  8. +
+
+

There is a lot more you can do:

+
    +
  • This is useful if you want to check the integrity of all internal and external links:

    +
    $ sphinx-build doc -W -b linkcheck _build
    +
    +
    +
  • +
  • sphinx-autobuild +provides a local web server that will automatically refresh your view +every time you save a file - which makes writing and testing much easier.

  • +
+
+
+

Demo: Building documentation with GitHub Actions

+

First we need to extend the environment.yml file to include the necessary packages:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - myst-parser
+  - sphinx
+  - sphinx-rtd-theme
+  - sphinx-autoapi
+
+
+

Then we add a GitHub Actions workflow .github/workflow/sphinx.yml to build the documentation:

+
name: Build documentation
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+permissions:
+  contents: write
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Sphinx build
+      run: |
+        sphinx-build doc _build
+      shell: bash -el {0}
+
+    - name: Deploy to GitHub Pages
+      uses: peaceiris/actions-gh-pages@v4
+      if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+      with:
+        publish_branch: gh-pages
+        github_token: ${{ secrets.GITHUB_TOKEN }}
+        publish_dir: _build/
+        force_orphan: true
+
+
+

Now:

+
    +
  • Add these two changes to the GitHub repository.

  • +
  • Go to “Settings” -> “Pages” -> “Branch” -> gh-pages -> “Save”.

  • +
  • Look at “Actions” tab and observe the workflow running and hopefully +deploying the website.

  • +
  • Finally visit the generated site. You find it by clicking the About wheel +icon on top right of your repository. There, select “Use your GitHub Pages +website”.

  • +
  • This is how we build almost all of our lesson websites (including this one).

  • +
  • Another popular place to deploy Sphinx documentation is ReadTheDocs.

  • +
+
+
+

Optional: How to auto-generate API documentation in Python

+

Add three tiny modifications (highlighted) to doc/conf.py to auto-generate API documentation +(this requires the sphinx-autoapi package):

+
project = "planets"
+copyright = "2024, Authors"
+author = "Authors"
+release = "0.1"
+
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+extensions = [
+    "myst_parser",  # in order to use markdown
+    "autoapi.extension",  # in order to use markdown
+]
+
+# search this directory for Python files
+autoapi_dirs = [".."]
+
+# ignore this file when generating API documentation
+autoapi_ignore = ["*/conf.py"]
+
+myst_enable_extensions = [
+    "colon_fence",  # ::: can be used instead of ``` for better rendering
+]
+
+html_theme = "sphinx_rtd_theme"
+
+
+

Then rebuild the documentation (or push the changes and let GitHub rebuild it) +and you should see a new section “API Reference”.

+
+
+

Confused about reStructuredText vs. Markdown vs. MyST?

+
    +
  • At the beginning there was reStructuredText and Sphinx was built for reStructuredText.

  • +
  • Independently, Markdown was invented and evolved into a couple of flavors.

  • +
  • Markdown became more and more popular but was limited compared to reStructuredText.

  • +
  • Later, MyST +was invented to be able to write +something that looks like Markdown but in addition can do everything that +reStructuredText can do with extra directives.

  • +
+
+
+

Where to read more

+ +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/example/index.html b/example/index.html new file mode 100644 index 0000000..c0c506a --- /dev/null +++ b/example/index.html @@ -0,0 +1,237 @@ + + + + + + + Example project: Simulating the motion of planets — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Example project: Simulating the motion of planets

+

The example code that we will study +is a hopefully simple N-body simulation written in Python. It is not important +or expected that we understand the code in any detail.

+

The big picture is that the code simulates the motion of a number of +planets:

+
    +
  • We can choose the number of planets.

  • +
  • Each planet starts with a random position, velocity, and mass.

  • +
  • At each time step, the code calculates the gravitational force between each +pair of planets.

  • +
  • The forces accelerate each planet, the acceleration modifies the velocity, +the velocity modifies the position of each planet.

  • +
  • We can choose the number of time steps.

  • +
  • The units were chosen to make numbers easy to read.

  • +
+
+

Example run

+
+

Instructor note

+

The instructor demonstrates running the code on their computer.

+
+

The code is written to accept command-line arguments to specify the number +of planets and the number of time steps.

+

We first generate starting data:

+
$ python generate-data.py --num-planets 10 --output-file initial.csv
+
+
+

The generated file (initial.csv) could look like this:

+
px,py,pz,vx,vy,vz,mass
+-46.88,-42.51,88.33,-0.86,-0.18,0.55,6.70
+-5.29,17.09,-96.13,0.66,0.45,-0.17,3.51
+83.53,-92.83,-68.77,-0.26,-0.48,0.24,6.84
+-36.31,25.48,64.16,0.85,0.75,-0.56,1.53
+-68.38,-17.21,-97.07,0.60,0.26,0.69,6.63
+-48.37,-48.74,3.92,-0.92,-0.33,-0.93,8.60
+40.53,-75.50,44.18,-0.62,-0.31,-0.53,8.04
+-27.21,10.78,-78.82,-0.09,-0.55,-0.03,5.35
+88.42,-74.95,-45.85,0.81,0.68,0.56,5.36
+39.09,53.12,-59.54,-0.54,0.56,0.07,8.98
+
+
+

Then we can simulate their motion (in this case for 20 steps):

+
$ python simulate.py --num-steps 20 \
+                     --input-file initial.csv \
+                     --output-file final.csv
+
+
+

The --output-file (final.csv) is again a CSV file (comma-separated values) +and contains the final positions of all planets.

+

It is possible to run on multiple cores and to animate the result. +Here is an example with 100 planets:

+
$ python generate-data.py --num-planets 100 --output-file initial.csv
+
+$ python simulate.py --num-steps 50 \
+                     --input-file initial.csv \
+                     --output-file final.csv \
+                     --trajectories-file trajectories.npz \
+                     --num-cores 8
+
+$ python animate.py --initial-file initial.csv \
+                    --trajectories-file trajectories.npz \
+                    --output-file animation.mp4
+
+
+
+

Learning goals

+
    +
  • What are the most important steps to make this code reusable by others +and our future selves?

  • +
  • Be able to apply these techniques to your own code/script.

  • +
+
+
+

We will not focus on …

+
    +
  • … how the code works internally in detail.

  • +
  • … whether this is the most efficient algorithm.

  • +
  • … whether the code is numerically stable.

  • +
  • … how to code scales with the number of cores.

  • +
  • … whether it is portable to other operating systems (we will discuss this later).

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/genindex/index.html b/genindex/index.html new file mode 100644 index 0000000..99f0d30 --- /dev/null +++ b/genindex/index.html @@ -0,0 +1,149 @@ + + + + + + Index — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..a97289d --- /dev/null +++ b/index.html @@ -0,0 +1,290 @@ + + + + + + + Reproducible research software development using Python — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Reproducible research software development using Python

+
+

Big-picture goal

+

This is a hands-on course on research software engineering. In this +workshop we assume that most workshop participants use Python in their work or +a leading a group which uses Python. Therefore, some of the examples will use +Python as the example language.

+

We will work with an example project and go through all the steps of a typical +software project. Once we have seen the building blocks, we will try to apply +them to own projects. Workshop participants will receive and also learn to give +constructive code feedback.

+
+
+

Prerequisites

+
+

Preparation

+
    +
  1. Get a GitHub account following these instructions.

  2. +
  3. You will need a text editor. If you don’t have a favorite one, we recommend +VS Code.

  4. +
  5. If you prefer to work in the terminal and not in VS Code, set up these two (skip this if you use VS Code):

    + +
  6. +
  7. One of these two software environments (if you are not sure which one to +choose or have no preference, choose Conda):

    + +
  8. +
  9. Optional and only on Linux: Apptainer following +these instructions.

  10. +
+
+
+
+

Schedule

+
+

Day 1

+ +
+
+

Day 2

+ +
+
+

Day 3

+ +
+
+

If we have time left

+ +
+
+
+
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/installation/conda/index.html b/installation/conda/index.html new file mode 100644 index 0000000..70fe17a --- /dev/null +++ b/installation/conda/index.html @@ -0,0 +1,252 @@ + + + + + + + Conda environment — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Conda environment

+

A Conda environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a environment.yml file that documents the dependencies:

+
name: coderefinery
+channels:
+  - conda-forge
+  - bioconda
+dependencies:
+  - python >= 3.10
+  - black
+  - click
+  - flit
+  - ipywidgets
+  - isort
+  - jupyterlab
+  - jupyterlab_code_formatter
+  - jupyterlab-git
+  - matplotlib
+  - myst-parser
+  - nbdime
+  - numpy
+  - pandas
+  - pytest
+  - pytest-cov
+  - scalene
+  - seaborn
+  - snakemake-minimal
+  - sphinx
+  - sphinx-autoapi
+  - sphinx-autobuild
+  - sphinx_rtd_theme >= 2.0
+  - vulture
+  - scikit-image
+
+
+
+

Before you create a virtual environment

+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create an environment.yml file and copy-paste the dependencies above into it.

  4. +
+
+
+

Choose the tool to manage the environment

+

If you are already using one of these tools, please continue using the tool that you like and know. +If you are new to this, we recommend using Miniconda or Miniforge.

+
    +
  • Anaconda

    +
      +
    • Advantages: easy to install, easy to use, good for beginners

    • +
    • Disadvantages: large download, installs more than we will need, license restrictions

    • +
    +
  • +
  • Miniconda

    +
      +
    • Advantages: small size, installs only what you need

    • +
    • Disadvantages: no graphical interface, license restrictions

    • +
    +
  • +
  • Miniforge

    +
      +
    • Advantages: small size, no license restrictions

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Micromamba

    +
      +
    • Advantages: fast, small size

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Pixi

    +
      +
    • Advantages: fast and new

    • +
    • Disadvantages: new and less tested and not documented here

    • +
    +
  • +
+
+
+

Creating the virtual environment

+
    +
  1. Open your terminal shell (e.g. Bash or Zsh).

  2. +
  3. Activate conda using conda activate or source ~/miniconda3/bin/activate.

  4. +
  5. Run the following command:

    +
    $ conda env create --file environment.yml
    +
    +
    +
  6. +
  7. Make sure that you see “coderefinery” in the output when you ask for a list of all available environments:

    +
    $ conda env list
    +
    +
    +
  8. +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/installation/virtual-environment/index.html b/installation/virtual-environment/index.html new file mode 100644 index 0000000..72439ff --- /dev/null +++ b/installation/virtual-environment/index.html @@ -0,0 +1,217 @@ + + + + + + + Virtual environment — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Virtual environment

+

A virtual environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a requirements.txt file that documents the dependencies:

+
black
+click
+flit
+ipywidgets
+isort
+jupyterlab
+jupyterlab-code-formatter
+jupyterlab-git
+matplotlib
+myst-parser
+nbdime
+numpy
+pandas
+pytest
+pytest-cov
+scalene
+seaborn
+sphinx
+sphinx-autoapi
+sphinx-autobuild
+sphinx_rtd_theme >= 2.0
+vulture
+scikit-image
+
+
+
+

Before you create a virtual environment

+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create a requirements.txt file and copy-paste the dependencies above into it.

  4. +
+
+
+

Creating the virtual environment

+

Now create a virtual environment in this directory either using pip and +venv +(more traditional and safer) or using uv (more modern but also less tested):

+
+

Create a new virtual environment and activate it:

+
$ python3 -m venv coderefinery-environment
+$ source coderefinery-environment/bin/activate
+
+
+

Install the dependencies into the environment:

+
$ python3 -m pip install -r requirements.txt
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/lesson.pdf b/lesson.pdf new file mode 100644 index 0000000..d032217 Binary files /dev/null and b/lesson.pdf differ diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000..31aff9c Binary files /dev/null and b/objects.inv differ diff --git a/profiling/index.html b/profiling/index.html new file mode 100644 index 0000000..ea98d96 --- /dev/null +++ b/profiling/index.html @@ -0,0 +1,164 @@ + + + + + + + Profiling memory and CPU usage — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Profiling memory and CPU usage

+

Profilers:

+
    +
  • cProfile or profile

  • +
  • line_profiler

  • +
  • viztracer

  • +
  • scalene

  • +
  • Intel VTune

  • +
  • PyInstrument

  • +
  • py-spy

  • +
  • Pyflame

  • +
  • timeit

  • +
+

(here I will add some examples that we can profile together)

+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/publishing/index.html b/publishing/index.html new file mode 100644 index 0000000..24ea5b1 --- /dev/null +++ b/publishing/index.html @@ -0,0 +1,161 @@ + + + + + + + How to release and publish your code — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

How to release and publish your code

+

Part 1:

+
    +
  • 30 min: Overview over software licenses and code publishing

  • +
  • 15 min: We add a license, CFF file, and publish to Zenodo (sandbox)

  • +
+

Part 2:

+
    +
  • 30 min: Creating a Python package with the help of flit

  • +
  • 15 min: Deploy the package to test-PyPI using GitHub Actions

  • +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/refactoring-concepts/index.html b/refactoring-concepts/index.html new file mode 100644 index 0000000..ca7705e --- /dev/null +++ b/refactoring-concepts/index.html @@ -0,0 +1,157 @@ + + + + + + + Concepts in refactoring and modular code design — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Concepts in refactoring and modular code design

+

Topics:

+
    +
  • Pure functions

  • +
  • Design patterns: functional design vs. object-oriented design

  • +
  • How to design your code before writing it: document-driven development

  • +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/refactoring-demo/index.html b/refactoring-demo/index.html new file mode 100644 index 0000000..235a7cd --- /dev/null +++ b/refactoring-demo/index.html @@ -0,0 +1,349 @@ + + + + + + + Demo: From a script towards a workflow — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Demo: From a script towards a workflow

+

In this episode we will explore code quality and good practices in Python using +a hands-on approach. We will together build up a small project and improve it +step by step.

+

We will start from a relatively simple image processing script which can read a +telescope image of stars and our goal is to count the number of stars in +the image. Later we will want to be able to process many such images.

+

The (fictional) telescope images look like the one below here (in this +repository we can find more):

+
+Generated image representing a telescope image of stars +
+

Generated image representing a telescope image of stars.

+
+
+
+

Rough plan for this demo

+
    +
  • (15 min) Discuss how we would solve the problem, run example code, and make it work (as part of a Jupyter notebook)?

  • +
  • (15 min) Refactor the positioning code into a function and a module

  • +
  • (15 min) Now we wish to process many images - discuss how we would approach this

  • +
  • (15 min) Introduce CLI and discuss the benefits

  • +
  • (30 min) From a script to a workflow (using Snakemake)

  • +
+
+ +
+

Plan

+

Topics we wish to show and discuss:

+
    +
  • Naming (and other) conventions, project organization, modularity

  • +
  • The value of pure functions and immutability

  • +
  • Refactoring (explained through examples)

  • +
  • Auto-formatting and linting with tools like black, vulture, ruff

  • +
  • Moving a project under Git

  • +
  • How to document dependencies

  • +
  • Structuring larger software projects in a modular way

  • +
  • Command-line interfaces

  • +
  • Workflows with Snakemake

  • +
+

We will work together on the code on the big screen, and participants will be +encouraged to give suggestions and ask questions. We will end up with a Git +repository which will be shared with workshop participants.

+
+
+

Possible solutions

+ + +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/reusable/index.html b/reusable/index.html new file mode 100644 index 0000000..b17c103 --- /dev/null +++ b/reusable/index.html @@ -0,0 +1,163 @@ + + + + + + + How to make the project more reusable — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

How to make the project more reusable

+

Part 1:

+
    +
  • 15 min: Motivation

  • +
  • 15 min: Best practices for managing dependencies in Python

  • +
  • 20 min: Exercise

  • +
+

Part 2:

+
    +
  • 10 min: Introduction to containers

  • +
  • 20 min: Demonstration

  • +
  • 10 min: Discussion

  • +
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/search/index.html b/search/index.html new file mode 100644 index 0000000..655dc57 --- /dev/null +++ b/search/index.html @@ -0,0 +1,163 @@ + + + + + + Search — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 0000000..abec3f0 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"(1) Basic browsing": [[20, "basic-browsing"]], "(1) Create a new branch and a new commit": [[19, "create-a-new-branch-and-a-new-commit"]], "(1) Opening an issue": [[5, "opening-an-issue"]], "(2) Compare commit history with network graph": [[20, "compare-commit-history-with-network-graph"]], "(2) Create a new branch.": [[5, "create-a-new-branch"]], "(2) Modify the file again with a new commit": [[19, "modify-the-file-again-with-a-new-commit"]], "(3) How can you browse the history of a single file?": [[20, "how-can-you-browse-the-history-of-a-single-file"]], "(3) Make a change adding the recipe": [[5, "make-a-change-adding-the-recipe"]], "(3) Switch to the main branch and create a commit there": [[19, "switch-to-the-main-branch-and-create-a-commit-there"]], "(4) Browse the commits you just made": [[19, "browse-the-commits-you-just-made"]], "(4) Push to GitHub as a new branch": [[5, "push-to-github-as-a-new-branch"]], "(4) Which files include the word \u201cposition\u201d?": [[20, "which-files-include-the-word-position"]], "(5) Compare the branches": [[19, "compare-the-branches"]], "(5) Open a pull request towards the main branch": [[5, "open-a-pull-request-towards-the-main-branch"]], "(5) Who modified each line last and when?": [[20, "who-modified-each-line-last-and-when"]], "(6) Can you use this code yourself? Are you allowed to share modifications?": [[20, "can-you-use-this-code-yourself-are-you-allowed-to-share-modifications"]], "(6) Compare two arbitrary commits": [[19, "compare-two-arbitrary-commits"]], "(6) Reviewing pull requests": [[5, "reviewing-pull-requests"]], "(7) Draft pull requests": [[5, "draft-pull-requests"]], "(7) Renaming a branch": [[19, "renaming-a-branch"]], "(8) Creating a tag": [[19, "creating-a-tag"]], "Adding the unit test to GitHub Actions": [[17, "adding-the-unit-test-to-github-actions"]], "Automated testing": [[17, null]], "Background": [[19, "background"]], "Before you create a virtual environment": [[10, "before-you-create-a-virtual-environment"], [11, "before-you-create-a-virtual-environment"]], "Big-picture goal": [[9, "big-picture-goal"]], "Choose the tool to manage the environment": [[10, "choose-the-tool-to-manage-the-environment"]], "Cloning a repository": [[2, "cloning-a-repository"]], "Code documentation": [[7, null]], "Collaborating within the same repository": [[5, null]], "Collaborative version control and code review": [[0, null]], "Commits, branches, repositories, forks, clones": [[2, "commits-branches-repositories-forks-clones"]], "Concepts around collaboration": [[2, null]], "Concepts in refactoring and modular code design": [[14, null]], "Conclict resolution, rebasing, and organizational strategies": [[3, null]], "Conda environment": [[10, null]], "Confused about reStructuredText vs. Markdown vs. MyST?": [[7, "confused-about-restructuredtext-vs-markdown-vs-myst"]], "Creating a copy of the repository by \u201cforking\u201d or \u201ccloning\u201d": [[20, "creating-a-copy-of-the-repository-by-forking-or-cloning"]], "Creating branches and commits": [[19, null]], "Creating the virtual environment": [[10, "creating-the-virtual-environment"], [11, "creating-the-virtual-environment"]], "Credit": [[6, null]], "Cross-referencing issues and pull requests": [[5, "cross-referencing-issues-and-pull-requests"]], "Day 1": [[9, "day-1"]], "Day 2": [[9, "day-2"]], "Day 3": [[9, "day-3"]], "Demo: Building documentation with GitHub Actions": [[7, "demo-building-documentation-with-github-actions"]], "Demo: From a script towards a workflow": [[15, null]], "Demonstration": [[21, "demonstration"]], "Discussion": [[17, "discussion-0"], [21, "discussion-0"]], "Di\u00e1taxis": [[7, "diataxis"]], "End-to-end tests": [[17, "end-to-end-tests"]], "Example project: Simulating the motion of planets": [[8, null]], "Example run": [[8, "example-run"]], "Exercise": [[1, "exercise"], [4, "exercise"], [5, "exercise"], [23, "exercise"]], "Exercise preparation": [[1, "prerequisites-0"], [4, "prerequisites-0"], [5, "prerequisites-0"], [20, "prerequisites-0"]], "Exercise: Browsing an existing project (20 min)": [[20, "exercise-0"]], "Exercise: Collaborating within the same repository (25 min)": [[4, "exercise-0"], [5, "exercise-0"]], "Exercise: Copy and browse an existing project": [[20, "exercise-copy-and-browse-an-existing-project"]], "Exercise: Creating branches and commits": [[19, "exercise-creating-branches-and-commits"]], "Exercise: Practice creating commits and branches (20 min)": [[19, "exercise-0"]], "Exercise: Practicing code review (25 min)": [[1, "exercise-0"]], "Exercise: Set up a Sphinx documentation": [[7, "exercise-set-up-a-sphinx-documentation"], [7, "exercise-0"]], "Exercise: Turn your project to a Git repo and share it (20 min)": [[23, "exercise-0"]], "Exercises": [[17, "exercises"]], "Features: roll-back, branching, merging, collaboration": [[21, "features-roll-back-branching-merging-collaboration"]], "Forking a repository": [[2, "forking-a-repository"]], "Forking, cloning, and browsing": [[20, null]], "GitHub, VS Code, or command line": [[20, "github-vs-code-or-command-line"]], "Help and discussion": [[1, "help-and-discussion"], [4, "help-and-discussion"]], "Help! I don\u2019t have permissions to push my local changes": [[4, "help-i-don-t-have-permissions-to-push-my-local-changes"]], "How about staging and committing?": [[22, "how-about-staging-and-committing"]], "How large should a commit be?": [[22, "how-large-should-a-commit-be"]], "How testing is often taught": [[17, "how-testing-is-often-taught"]], "How to approach other people\u2019s repositories with ideas, changes, and requests": [[4, "how-to-approach-other-peoples-repositories-with-ideas-changes-and-requests"]], "How to ask for changes in a pull request": [[1, "how-to-ask-for-changes-in-a-pull-request"]], "How to contribute changes to repositories that belong to others": [[4, null]], "How to create a fork": [[20, null]], "How to make the project more reusable": [[16, null]], "How to modify a pull request to address the review comments": [[1, "how-to-modify-a-pull-request-to-address-the-review-comments"]], "How to release and publish your code": [[13, null]], "How to suggest a change in a pull request as a reviewer": [[1, "how-to-suggest-a-change-in-a-pull-request-as-a-reviewer"]], "How to update your fork with changes from upstream": [[4, "how-to-update-your-fork-with-changes-from-upstream"]], "If we have time left": [[9, "if-we-have-time-left"]], "In-code documentation": [[7, "in-code-documentation"]], "Instructor note": [[7, "instructor-note-0"], [8, "instructor-note-0"], [17, "instructor-note-0"], [20, "instructor-note-0"]], "Introduction to version control with Git and GitHub": [[18, null]], "Is putting software on GitHub/GitLab/\u2026 publishing?": [[23, "is-putting-software-on-github-gitlab-publishing"]], "Keypoints": [[7, "keypoints-0"]], "Learning goals": [[8, null]], "Longer answer": [[17, "solution-0"]], "Many languages allow \u201cdocstrings\u201d": [[7, "many-languages-allow-docstrings"]], "Motivation": [[4, null], [4, null], [17, "motivation"], [21, null]], "Objectives": [[2, "objectives-0"], [7, "objectives-0"], [17, "objectives-0"], [19, "objectives-0"], [20, "objectives-0"], [21, "objectives-0"], [23, "objectives-0"]], "Often a README is enough - checklist": [[7, "often-a-readme-is-enough-checklist"]], "Opening a pull request towards the upstream repository": [[4, "opening-a-pull-request-towards-the-upstream-repository"]], "Optional: How to auto-generate API documentation in Python": [[7, "optional-how-to-auto-generate-api-documentation-in-python"]], "Optional: How to turn your project to a Git repo and share it": [[23, null]], "Plan": [[15, "plan"]], "Possible solutions": [[15, "possible-solutions"]], "Practical advice: How much Git is necessary?": [[22, null]], "Practicing code review": [[1, null]], "Preparation": [[7, "prerequisites-0"], [9, "prerequisites-0"]], "Prerequisites": [[9, "prerequisites"]], "Profiling memory and CPU usage": [[12, null]], "Pull requests can be coupled with automated testing": [[4, "pull-requests-can-be-coupled-with-automated-testing"]], "Pytest": [[17, "pytest"]], "Recovery": [[4, "solution-0"]], "Reproducible research software development using Python": [[9, null]], "Rough plan for this demo": [[15, null]], "Schedule": [[9, "schedule"]], "Script after some work, with command-line interface (spoiler alert)": [[15, "solution-1"]], "Searching in a forked repository will not work instantaneously!": [[20, null]], "Snakemake rules which define a workflow (spoiler alert)": [[15, "solution-2"]], "Solution": [[21, "solution-0"]], "Solution and hints": [[5, "solution-and-hints"]], "Solution and walk-through": [[19, "solution-and-walk-through"], [20, "solution-and-walk-through"]], "Starting point (spoiler alert)": [[15, "solution-0"]], "Summary": [[1, "summary"], [4, "summary"], [5, "summary"], [19, "summary"], [20, "summary"]], "Synchronizing changes between repositories": [[2, "synchronizing-changes-between-repositories"]], "Talking about code": [[21, "talking-about-code"]], "Virtual environment": [[11, null]], "We will not focus on \u2026": [[8, null]], "What else is possible": [[17, "what-else-is-possible"]], "What if you need more than a README?": [[7, "what-if-you-need-more-than-a-readme"]], "What is a protected branch? And how to modify it?": [[5, "what-is-a-protected-branch-and-how-to-modify-it"]], "What level of branching complexity is necessary for each project?": [[22, "what-level-of-branching-complexity-is-necessary-for-each-project"]], "What to avoid": [[22, "what-to-avoid"]], "What we typically like to snapshot": [[21, "what-we-typically-like-to-snapshot"]], "Where to read more": [[7, "where-to-read-more"]], "Where to start": [[17, "where-to-start"]], "Why do we need to keep track of versions?": [[21, "why-do-we-need-to-keep-track-of-versions"]], "Why? \ud83d\udc97\u2709\ufe0f to your future self": [[7, "why-to-your-future-self"]], "Working on the command line? Use \u201cgit status\u201d all the time": [[22, "working-on-the-command-line-use-git-status-all-the-time"]], "Writing useful commit messages": [[22, "writing-useful-commit-messages"]]}, "docnames": ["collaboration", "collaboration/code-review", "collaboration/concepts", "collaboration/demo-discussion", "collaboration/forking-workflow", "collaboration/same-repository", "credit", "documentation", "example", "index", "installation/conda", "installation/virtual-environment", "profiling", "publishing", "refactoring-concepts", "refactoring-demo", "reusable", "testing", "version-control", "version-control/branching-and-committing", "version-control/browsing", "version-control/motivation", "version-control/practical-advice", "version-control/sharing"], "envversion": {"sphinx": 62, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["collaboration.md", "collaboration/code-review.md", "collaboration/concepts.md", "collaboration/demo-discussion.md", "collaboration/forking-workflow.md", "collaboration/same-repository.md", "credit.md", "documentation.md", "example.md", "index.md", "installation/conda.md", "installation/virtual-environment.md", "profiling.md", "publishing.md", "refactoring-concepts.md", "refactoring-demo.md", "reusable.md", "testing.md", "version-control.md", "version-control/branching-and-committing.md", "version-control/browsing.md", "version-control/motivation.md", "version-control/practical-advice.md", "version-control/sharing.md"], "indexentries": {}, "objects": {}, "objnames": {}, "objtypes": {}, "terms": {"": [1, 5, 17, 19, 20, 22, 23], "0": [7, 8, 10, 11, 15, 17, 19, 22, 23], "00": 9, "03": 8, "04": 8, "07": 8, "08": 23, "09": [8, 9, 21], "1": [7, 8, 13, 15, 16, 17], "10": [8, 9, 10, 16, 17], "100": [8, 21, 23], "12": [7, 8, 9, 17, 23], "123": 22, "13": [8, 9], "1343ac0": 21, "14": 9, "15": [7, 9, 13, 15, 16, 17, 22], "16": [8, 9], "17": [8, 23], "18": [8, 9], "19": 21, "19k": 23, "2": [7, 10, 11, 13, 16, 17, 22, 23], "20": [8, 9, 16, 17], "2013": 7, "2019": 21, "2020": 21, "2021": 21, "2023": 21, "2024": [7, 21], "21": [8, 21, 23], "24": 8, "244c993": 20, "25": 8, "26": 8, "27": 8, "273": 7, "28": 23, "29": 8, "3": [7, 8, 10, 15, 17, 23], "30": [7, 9, 13, 15], "300": 15, "31": 8, "33": 8, "35": 8, "36": [8, 23], "37": 8, "38": 8, "39": 8, "4": [15, 23], "40": [3, 8, 9], "42": 8, "44": 8, "45": [8, 9], "46": 8, "48": 8, "5": [7, 8, 15, 17], "50": [7, 8, 9, 22], "5000": 4, "51": 8, "53": 8, "54": 8, "55": 8, "56": 8, "59": 8, "6": [8, 23], "60": 8, "62": 8, "63": 8, "64": 8, "66": 8, "68": 8, "69": 8, "7": 23, "70": 8, "74": 8, "75": 8, "77": 8, "78": 8, "8": [7, 8, 15, 17], "81": 8, "82": 8, "83": 8, "84": 8, "85": 8, "86": 8, "88": 8, "9": 4, "90": 9, "92": 8, "93": 8, "95": 8, "96": 8, "97": 8, "98": 8, "A": [1, 2, 5, 9, 10, 11, 17, 19, 20, 22], "And": 1, "As": 1, "At": [7, 8, 20], "BY": 6, "Be": [2, 5, 8, 23], "Being": [1, 4, 5], "But": [1, 4, 5, 17, 20, 21, 22, 23], "By": 20, "For": [5, 7, 19, 23], "If": [1, 4, 5, 7, 10, 17, 20, 22, 23], "In": [1, 2, 4, 5, 9, 10, 11, 15, 19, 20, 21, 23], "It": [1, 2, 4, 5, 7, 8, 17, 20, 21, 22, 23], "No": 17, "Not": [7, 19, 22], "On": [19, 20, 22], "One": 9, "Or": [1, 4, 21, 22], "The": [1, 2, 4, 5, 6, 7, 8, 15, 17, 19, 20, 22, 23], "Then": [4, 5, 7, 8, 19, 23], "There": 7, "These": [4, 20, 21], "To": [4, 5, 20, 23], "With": [19, 21], "__main__": 15, "__name__": 15, "__pycache__": 22, "_build": 7, "abl": [1, 2, 4, 5, 7, 8, 15, 23], "about": [1, 4, 5, 19, 23], "abov": [1, 10, 11, 17, 19, 20, 23], "acceler": 8, "accept": [1, 4, 5, 8, 22], "access": [2, 4, 5, 20, 23], "accident": [1, 5, 23], "account": [2, 4, 5, 9, 20, 23], "achiev": [9, 19], "across": 2, "action": [4, 13], "activ": [5, 10, 11], "actual": [9, 19, 20], "ad": [1, 4, 7, 19], "add": [1, 5, 7, 12, 13, 17, 19, 20, 22, 23], "addit": [7, 19, 23], "address": [5, 23], "adipisc": 7, "adjust": [4, 5, 7, 15, 19, 22], "advanc": 20, "advantag": 10, "advic": [9, 18, 19], "affect": 21, "after": [1, 4, 20, 23], "again": [1, 4, 5, 8, 17, 22], "agreement": 4, "algorithm": 8, "alia": 20, "aliqua": 7, "aliquip": 7, "all": [2, 5, 6, 7, 8, 9, 10, 15, 17, 19, 20, 23], "allclos": 17, "allow": 5, "almost": 7, "along": 20, "alpha": [15, 22], "alreadi": [4, 10, 19, 20, 23], "also": [1, 2, 4, 5, 6, 7, 9, 11, 17, 19, 20, 22, 23], "altern": [2, 5, 20], "alwai": [1, 4, 21, 22], "am": 21, "ambiti": 22, "amend": 1, "amet": 7, "an": [1, 4, 7, 8, 9, 10, 11, 17, 19, 21, 23], "anaconda": 10, "ani": [4, 7, 8, 17, 19, 22, 23], "anim": [7, 8], "annot": [20, 21], "anoth": [7, 19, 20], "answer": [19, 20, 21], "anticip": 21, "anyon": 20, "appear": 19, "appli": [8, 9, 15], "applic": 20, "approach": [7, 15], "apptain": 9, "ar": [1, 2, 4, 5, 7, 8, 9, 10, 19, 21, 22, 23], "arbitrari": 17, "area": 22, "aren": [19, 20], "argument": [8, 17], "around": [0, 9, 19], "arrai": 17, "arrow": 23, "articl": 5, "ask": [4, 5, 10, 15], "assert": [7, 17], "assum": 9, "attach": [2, 19], "attent": 4, "august": [7, 21], "aut": 7, "authent": [1, 4, 5], "author": [7, 20, 21, 22, 23], "auto": 15, "autoapi": [7, 10, 11], "autoapi_dir": 7, "autoapi_ignor": 7, "autobuild": [7, 10, 11], "autom": 9, "automat": [1, 2, 4, 5, 7, 17, 19], "avail": [9, 10, 15], "avoid": 20, "awar": 20, "b": [7, 17, 23], "back": [2, 19, 20], "background": 15, "backup": 23, "bad": [21, 22], "base": [2, 15, 17, 22], "bash": [7, 10, 17, 20], "basi": 5, "basic": 19, "becam": 7, "becaus": [15, 22, 23], "becom": 20, "been": 6, "befor": [4, 5, 14, 19, 20, 21, 22], "begin": 7, "beginn": 10, "behind": 4, "being": 4, "belong": [0, 9], "below": [1, 4, 5, 7, 15, 19, 20, 23], "benefit": 15, "best": 16, "better": [1, 7, 17, 20, 21, 22], "between": [5, 8, 19, 21], "big": [8, 15, 20], "bin": [10, 11], "binari": 15, "binary_imag": 15, "bioconda": 10, "black": [10, 11, 15], "blame": 20, "blob": [17, 21], "block": [7, 9], "blogdown": 7, "bodi": 8, "book": [4, 5], "bookdown": 7, "both": [4, 5, 19, 20], "bottom": [5, 19, 20], "branch": [1, 4, 7, 9, 17, 18, 20, 23], "brand": 23, "break": [9, 17, 21, 22], "brief": 19, "bright": 15, "broke": [17, 21], "brows": [9, 17, 18, 21, 22], "browser": [7, 19, 20], "bug": 21, "build": [9, 15, 17, 19, 20], "built": 7, "bunch": [20, 23], "button": [1, 19, 20, 23], "c": 20, "c7f0e8bfc718be04525847fc7ac237f470add76": 2, "cach": [7, 17], "calcul": 8, "call": [2, 5, 19, 20, 23], "can": [1, 2, 5, 7, 8, 12, 15, 17, 19, 21, 22, 23], "cannot": 5, "caption": 7, "carri": 21, "case": [1, 2, 4, 5, 7, 8, 17, 20, 23], "catch": 17, "cc": 6, "cd": 20, "celsiu": 7, "central": 2, "centroid": 15, "certain": [19, 21], "cff": 13, "chang": [0, 6, 7, 9, 17, 19, 20, 21, 22, 23], "changelog": 22, "channel": [7, 10, 15, 17], "check": [4, 5, 7, 17, 23], "checklist": 5, "checkout": [7, 17], "checksum": 21, "choic": 22, "choos": [8, 9, 23], "chose": 23, "chosen": 8, "cillum": 7, "citabl": 23, "citat": [7, 17], "class": 20, "cleanup": [7, 17], "clear": 4, "cli": 15, "click": [1, 4, 5, 7, 10, 11, 15, 17, 19, 20, 23], "clone": [4, 5, 7, 9, 18, 21], "close": [1, 4, 5], "cloud": 5, "cmap": 15, "code": [2, 4, 5, 8, 9, 11, 15, 17, 19, 22, 23], "codecov": 17, "coderefineri": [6, 7, 10, 11], "collabor": [1, 6, 9, 23], "colleagu": 2, "collect": [5, 15, 20, 22], "colon_f": 7, "color": 15, "com": [4, 5, 7, 17, 19, 20, 21, 22, 23], "combin": [1, 22], "come": [5, 20], "comfort": 20, "comma": [8, 15], "command": [2, 5, 8, 10, 19, 23], "commemor": 2, "comment": [5, 7], "commentari": 7, "commit": [1, 4, 5, 9, 17, 18, 21, 23], "commodo": 7, "common": [1, 2, 19], "commun": [2, 5, 9], "compar": [7, 17, 21, 22], "complet": [2, 22, 23], "complic": 17, "compon": 17, "compress": 23, "comput": [2, 4, 8, 20, 21, 23], "concept": [0, 9], "conceptu": 21, "conclict": [0, 9], "conda": [7, 9, 17], "conf": 7, "config": 20, "configur": [5, 21, 23], "conflict": 9, "connect": [5, 9, 15], "consectetur": 7, "consequat": 7, "consid": [5, 22, 23], "consist": 23, "constant": [17, 20], "construct": [1, 5, 7, 9], "contain": [2, 4, 8, 9, 16, 22, 23], "content": 7, "context": [1, 20, 22], "continu": [1, 10, 20], "contribut": [0, 2, 5, 7, 9], "control": [7, 9, 19, 20, 21, 23], "convent": [15, 22], "converg": 22, "convert": 7, "convert_to_grai": 15, "copi": [2, 5, 7, 10, 11, 19, 21, 23], "copyright": [5, 7], "core": [8, 15], "corner": 5, "correct": [4, 23], "correspond": [17, 20], "cost": 19, "could": [1, 2, 5, 8, 19, 20, 23], "count": [15, 23], "counter": 1, "coupl": 7, "cours": [9, 10, 11, 20], "cov": [10, 11, 17], "cover": 7, "coverag": 17, "coveral": 17, "cprofil": 12, "cpu": 9, "creat": [1, 2, 4, 6, 7, 9, 13, 15, 17, 18, 21, 22, 23], "cross": [4, 15, 22], "csv": 8, "culpa": 7, "cupidatat": 7, "current": [19, 23], "dai": [5, 22], "danger": [22, 23], "data": [2, 8, 17, 21], "date": [4, 21], "db": 7, "debrief": 9, "decad": 22, "decid": [2, 10, 11], "decor": [19, 20], "def": [7, 15, 17], "default": [4, 15, 19, 20, 21, 23], "defin": [4, 19, 20], "degre": 7, "delet": 5, "deliber": 1, "delta": 23, "demo": [3, 9], "demonstr": [4, 8, 16, 20], "depend": [5, 7, 9, 10, 11, 15, 16, 17, 20], "deploi": [7, 13, 17], "describ": [4, 5, 17, 20], "descript": [5, 23], "deserunt": 7, "design": [9, 17, 19, 20, 22], "detail": [1, 4, 7, 8, 22], "detect": [15, 22], "develop": [2, 14, 19, 22], "diataxi": 7, "did": [19, 21], "didn": 19, "diff": 19, "differ": [2, 4, 19, 20, 21, 23], "difficult": [4, 17], "direct": [4, 5, 7], "directli": [1, 5, 19], "directori": [7, 10, 11, 17, 20, 21, 22, 23], "disadvantag": 10, "discard": 21, "discov": 21, "discuss": [2, 3, 7, 8, 9, 15, 16, 20, 22], "disk": 19, "distanc": 17, "distribut": 9, "diverg": 19, "divid": 2, "do": [1, 2, 4, 5, 7, 19, 20, 22, 23], "doc": [7, 21], "document": [6, 9, 10, 11, 14, 15, 21], "doe": [4, 7, 17, 19], "doi": 23, "dolor": 7, "don": [1, 5, 7, 9, 19, 20, 23], "done": [4, 5, 19, 20, 23], "dot": [19, 20], "doubl": 4, "down": [5, 23], "download": [2, 10], "dpi": 15, "driven": 14, "ds_store": 7, "dui": 7, "dure": [1, 4, 22], "e": [2, 7, 10, 20], "ea": 7, "each": [1, 2, 4, 5, 8, 19], "earli": [5, 22], "easi": [1, 8, 10, 19, 20, 23], "easier": [7, 17, 22], "easili": [19, 22], "edit": 19, "editor": [9, 20], "effici": 8, "effort": 5, "either": [1, 11, 19, 20, 23], "eiusmod": 7, "el": [7, 17], "elit": 7, "els": [1, 5, 22], "email": 5, "empti": [22, 23], "emul": 7, "en": 5, "enabl": [1, 22], "encourag": [5, 15, 19, 20], "end": [5, 15, 22], "enemi": 22, "engin": [6, 9, 22], "enim": 7, "enough": [19, 20, 22], "enter": [19, 20], "entir": 19, "enumer": 23, "env": 10, "environ": [7, 9, 17], "episod": [1, 4, 5, 7, 15, 19, 20], "equat": 7, "error": [4, 7, 23], "especi": 22, "ess": 7, "est": 7, "et": 7, "etc": [9, 22], "eu": 7, "even": [1, 5, 23], "event_nam": 7, "everi": [7, 17, 20], "everyon": 2, "everyth": 7, "evolv": 7, "ex": 7, "exactli": 4, "exampl": [4, 5, 7, 9, 12, 15, 17, 19, 20, 21, 22, 23], "excepteur": 7, "exclude_pattern": 7, "execut": 23, "exercis": 16, "exercit": 7, "exist": [4, 15, 19, 21, 23], "expand": 15, "expect": [4, 8, 17, 19], "experi": [7, 17, 21], "expert": [5, 23], "explain": [7, 15, 23], "explan": [17, 23], "explor": [7, 15, 20], "extend": [7, 17], "extens": [7, 19, 20], "extern": 7, "extra": [1, 7], "extract": 15, "f": 15, "fact": 1, "fail": [4, 17], "fals": [7, 15, 17], "familiar": [1, 4, 5], "fast": 10, "faster": 22, "favorit": 9, "favourit": 22, "featur": [4, 22], "februari": 21, "feedback": [4, 5, 9], "feel": [7, 17], "fetch": [2, 4, 5], "few": [1, 2, 4, 20, 22, 23], "fiction": 15, "field": 1, "figsiz": 15, "figur": 15, "file": [1, 2, 5, 7, 8, 10, 11, 13, 15, 17, 21, 22, 23], "file_nam": 15, "filenam": [19, 20], "filter": 15, "final": [7, 8, 19, 23], "find": [7, 15, 19, 20, 23], "findabl": 23, "fine": 23, "finish": [5, 23], "first": [2, 4, 5, 7, 8, 17, 19, 20, 22, 23], "fix": [1, 4, 5, 22], "flask": 22, "flavor": 7, "flit": [10, 11, 13], "float": 7, "folder": [20, 23], "follow": [6, 7, 9, 10, 15, 19, 20, 21, 23], "forc": [5, 8, 17, 23], "force_between_planet": [17, 21], "force_magnitud": 17, "force_orphan": 7, "forg": [7, 10, 17], "forget": [5, 7], "fork": [7, 9, 18, 21], "form": [5, 19, 22], "format": 15, "formatt": 11, "former": 2, "found": [15, 21], "fourth": 15, "fr": 7, "fragment": 1, "free": [7, 22], "frequent": 19, "from": [1, 2, 5, 7, 9, 17, 19, 20, 21, 22, 23], "fugiat": 7, "fulli": [22, 23], "fun": 4, "function": [7, 14, 15, 17, 19, 20], "futur": [5, 8, 22], "g": [2, 7, 10, 17, 20], "gate": 1, "gaussian": 15, "gener": [2, 8, 15, 17, 19, 22], "generate_plot": 15, "get": [1, 2, 4, 5, 7, 9, 15, 17, 19, 20, 22, 23], "ggplot2": 22, "gh": 7, "git": [2, 4, 5, 6, 7, 9, 10, 11, 15, 19, 20, 21], "github": [1, 2, 4, 6, 9, 13, 19, 21], "github_token": 7, "gitignor": [19, 22, 23], "gitlab": [2, 4, 5, 7, 21], "give": [1, 5, 9, 15, 21], "given": 17, "glob_wildcard": 15, "global": 20, "go": [5, 7, 9, 19, 20, 21, 23], "goal": [1, 15, 17], "goe": [19, 20], "gone": 21, "good": [1, 5, 7, 9, 10, 15, 17, 22, 23], "googl": 21, "gopher": [19, 21], "grab": 4, "grai": 15, "grant": 4, "graph": [19, 21], "graphic": 10, "gravit": [8, 17, 20], "gray_imag": 15, "great": [21, 22], "green": 23, "grep": 20, "group": [1, 5, 9], "guarante": 17, "guid": [7, 17], "ha": [2, 5, 19, 20, 22], "half": 5, "hand": [9, 15], "happen": [5, 19, 23], "hard": 22, "harder": 1, "hash": 19, "have": [1, 2, 5, 6, 7, 17, 19, 20, 21, 23], "head": 7, "hear": 5, "help": [5, 13, 15, 22, 23], "here": [1, 4, 5, 8, 10, 12, 15, 19, 23], "high": 2, "highlight": [7, 17], "hint": [1, 19, 20], "histor": 20, "histori": [1, 2, 23], "hold": 17, "hopefulli": [7, 8, 21], "host": 21, "hover": 20, "how": [0, 2, 8, 9, 14, 15, 18, 19, 21], "howev": 5, "html": 7, "html_theme": 7, "http": [4, 5, 6, 7, 9, 17, 19, 20, 21, 22, 23], "huge": 22, "hugo": 7, "human": 5, "hypothesi": 17, "i": [1, 2, 6, 8, 9, 10, 11, 12, 15, 18, 19, 20, 21], "icon": [5, 7, 19, 23], "id": 7, "idea": [21, 22], "ideal": 5, "identifi": [2, 19, 20, 23], "ignor": [7, 15], "illustr": [19, 20], "imag": [4, 7, 10, 11, 15, 19, 21], "image_fil": 15, "image_smooth": 15, "imagin": 15, "immut": 15, "imperfect": 22, "implement": [1, 2], "import": [8, 15, 19, 20, 22], "imposs": 7, "improv": [1, 7, 9, 15, 22], "imread": 15, "imshow": 15, "inbox": 5, "incididunt": 7, "includ": [5, 7], "incomplet": 1, "increas": 22, "incredibli": 5, "independ": [2, 7], "index": [7, 20], "indic": 4, "individu": 9, "info": 1, "inform": [4, 5, 9, 19, 20, 22], "ingredi": 4, "init": [7, 17, 23], "initi": [8, 23], "input": [8, 15], "insensit": [5, 20], "insid": 20, "insight": [19, 20, 21], "inspect": 20, "inspir": [19, 21, 22], "instal": [5, 7, 10, 11], "instanc": [2, 17, 20], "instead": [1, 5, 7, 17, 20, 23], "instruct": [4, 5, 7, 9, 19], "instructor": [4, 5], "integr": 7, "intel": 12, "intent": 2, "interact": 5, "interchang": [5, 19], "interest": [1, 5], "interf": 21, "interfac": [1, 5, 10, 19, 23], "interfer": 19, "intern": [7, 8], "intro": 6, "introduc": [1, 15], "introduct": [9, 16], "invent": 7, "invit": 5, "io": [6, 15], "ipsum": 7, "ipywidget": [10, 11], "irur": 7, "is_flag": 15, "isn": 1, "isol": [10, 11], "isort": [10, 11], "issu": [4, 20, 22], "its": 17, "itself": 7, "jekyl": 7, "job": [7, 17], "john": 7, "join": 17, "joss": 7, "julia": 22, "jupyt": 15, "jupyterlab": [10, 11], "jupyterlab_code_formatt": 10, "just": [1, 4, 5, 7, 20, 22, 23], "keep": [1, 4, 7], "kei": 7, "kelvin": 7, "kelvin_to_celsiu": 7, "kept": 7, "keyword": [5, 20], "kib": 23, "kill": 17, "kind": [1, 4, 5, 21], "knitr": 7, "know": [1, 4, 5, 10, 17, 20, 21, 22], "l": 23, "l31c5": 21, "l39": 21, "label": 15, "labeled_imag": 15, "labor": 7, "labori": 7, "laborum": 7, "languag": [9, 17], "laptop": [2, 21], "larg": [10, 23], "larger": [1, 15], "later": [1, 2, 4, 5, 7, 8, 15, 19, 20, 21, 22], "latest": [2, 7, 17, 21], "latex": 21, "layout": 22, "lead": 9, "learn": [1, 2, 4, 5, 7, 9, 17, 22], "learner": 5, "least": [1, 7], "leav": 23, "left": [5, 19, 20], "len": 15, "less": [10, 11, 17], "lesson": [1, 4, 7], "let": [4, 7, 17, 19, 22, 23], "level": 2, "leverag": 7, "licens": [5, 7, 9, 10, 13, 20, 23], "like": [1, 2, 4, 5, 7, 8, 10, 15, 17, 19, 20, 22, 23], "limit": [7, 17], "line": [1, 2, 4, 5, 8, 17, 19, 23], "line_profil": 12, "linear": 20, "link": [4, 5, 7, 23], "linkcheck": 7, "lint": 15, "linux": 9, "list": [10, 20, 21], "ll": [19, 20], "local": [1, 5, 7, 17, 20, 23], "locat": [10, 11, 19], "locate_posit": 15, "log": [15, 19, 20, 23], "login": 23, "long": 20, "longer": 22, "look": [1, 4, 7, 8, 15, 17, 19, 20], "lorem": 7, "lose": 22, "lot": [7, 21, 23], "low": 7, "m": [11, 19, 23], "made": [2, 5, 6], "magna": 7, "magnifi": 20, "mai": [7, 19, 22], "main": [1, 2, 4, 7, 15, 17, 20, 22, 23], "mainli": 5, "maintain": 5, "make": [1, 2, 4, 7, 8, 9, 10, 15, 17, 19, 20, 21, 22, 23], "mamba": [7, 17], "manag": [5, 11, 16], "mani": [5, 15, 22], "manuscript": [19, 21], "mar": 23, "mark": [5, 19, 22], "markeredgewidth": 15, "markers": 15, "markup": 7, "mass": 8, "mass1": 17, "mass2": 17, "master": [2, 19, 23], "match": [17, 20], "materi": [4, 6, 7, 17, 20, 21], "math": 7, "matplotlib": [7, 10, 11, 15, 17], "maxdepth": 7, "mayb": 4, "md": [7, 19], "me": [19, 21, 22], "mean": [1, 2, 5, 19, 23], "meaning": 19, "meant": [5, 20], "measur": [7, 15], "mechan": [1, 2], "memori": 9, "mention": 5, "merg": [1, 2, 4, 5, 9, 19, 20, 22], "messag": [5, 19, 20, 21], "metadata": [19, 21], "method": [19, 23], "mib": 23, "micromamba": [7, 10, 17], "might": [4, 20, 22, 23], "mileston": 22, "min": [3, 7, 9, 13, 15, 16, 17], "mind": 23, "mini": 6, "miniconda": 10, "miniconda3": 10, "miniforg": 10, "minim": [7, 10], "minor": 4, "minu": 1, "minut": 20, "mistak": 1, "mkdoc": 7, "modern": [4, 11], "modif": [4, 7, 19], "modifi": 8, "modul": [15, 17], "modular": [9, 15], "mollit": 7, "more": [1, 9, 10, 11, 15, 17, 19, 20, 21, 22], "most": [5, 8, 9, 19, 20, 22], "motion": 9, "motiv": [9, 16, 18, 22], "mous": 4, "move": [15, 19], "mp4": 8, "much": [1, 4, 5, 7, 9, 17, 18, 19, 20, 21], "multipl": [2, 8, 19, 20, 22], "must": 5, "mutant": 17, "mutat": 17, "mutmut": 17, "my": [5, 21, 22, 23], "myproject": [21, 23], "myscript": 23, "myst": [10, 11], "myst_enable_extens": 7, "myst_pars": 7, "myvers": 21, "n": [8, 15], "name": [5, 7, 10, 15, 17, 19, 20, 23], "navig": [4, 20], "nbdime": [10, 11], "necessari": [1, 2, 5, 7, 9, 18, 19], "need": [1, 2, 5, 9, 10, 11, 17, 19, 20, 22, 23], "neg": 7, "network": [19, 21], "never": [22, 23], "new": [1, 2, 4, 7, 10, 11, 17, 20, 22, 23], "newaddress": 5, "newfeatur": 21, "next": [1, 17, 19, 23], "nice": [5, 19, 20], "nisi": 7, "nois": 15, "non": 7, "none": 23, "normal": 19, "nostrud": 7, "note": [4, 5, 19, 22, 23], "notebook": [15, 17], "noth": 17, "notic": 4, "notif": 5, "notifi": 5, "now": [1, 4, 5, 7, 11, 15, 17, 19, 20, 21, 22, 23], "np": 17, "npz": 8, "nulla": 7, "num": 8, "number": [1, 4, 5, 8, 15], "numer": 8, "numpi": [7, 10, 11, 17, 22], "oauth": 23, "object": [14, 15], "observ": [4, 5, 7], "occaecat": 7, "occurr": 20, "offer": [19, 20, 23], "officia": 7, "often": [2, 5, 19, 22], "oh": 21, "old": 17, "older": 23, "onc": [1, 4, 5, 9, 19, 22, 23], "one": [1, 2, 4, 5, 7, 9, 10, 15, 17, 19, 20, 22, 23], "onelin": [19, 20], "onli": [1, 5, 7, 9, 10, 19, 20, 22, 23], "onlin": [15, 23], "oop": 22, "open": [1, 7, 10, 15, 19, 20, 23], "oper": [2, 8], "opinion": 7, "option": [9, 15, 18, 20], "order": [1, 2, 7], "org": [7, 17], "organ": [5, 15, 20], "organiz": [0, 9], "orient": 14, "origin": [4, 5, 6, 7, 15, 20, 23], "other": [0, 1, 2, 5, 7, 8, 9, 15, 19, 20, 21, 22, 23], "otherwis": [1, 22], "our": [1, 5, 7, 8, 15, 17, 19, 20, 21, 23], "out": [1, 4, 7, 19, 20, 21, 22], "output": [7, 8, 10, 15, 20], "output_fil": 15, "over": [13, 19, 20, 22], "overlai": 15, "overview": [7, 13, 19], "overwrit": 23, "own": [2, 5, 7, 8, 9, 17, 19, 20, 21, 23], "p": 22, "pack": 23, "packag": [7, 9, 13], "page": [5, 7, 19, 20, 23], "pai": 4, "pair": [8, 20], "panda": [10, 11, 22], "paper": [2, 21], "paragraph": 22, "parallel": 21, "paramet": 7, "parent": 2, "pariatur": 7, "parser": [7, 10, 11], "part": [1, 5, 7, 13, 15, 16, 17, 19, 23], "particip": [5, 9, 15], "particular": 2, "pass": [4, 17], "past": [7, 10, 11, 20, 23], "path": [15, 20, 23], "pattern": [14, 19], "peaceiri": 7, "pencil": 19, "peopl": [5, 17, 19, 20, 21, 22], "perfect": [17, 22], "perhap": 2, "permalink": 21, "permiss": 7, "persist": 23, "person": [2, 20, 22], "phd": 2, "pictur": [8, 20], "pid": 23, "piec": [5, 15, 20], "pip": 11, "pixi": 10, "place": [5, 7], "placehold": 23, "plain": 21, "planet": [7, 9, 17, 19, 20, 21], "plaqu": 2, "platform": 21, "pleas": [1, 4, 5, 10, 19, 21, 23], "plot": 15, "plot_posit": 15, "plt": 15, "plu": [1, 23], "png": 15, "point": [4, 5, 7, 17, 19, 21, 23], "pointer": 2, "popular": [5, 7], "portabl": 8, "posit": [8, 15], "position1": 17, "position2": 17, "possibl": [1, 4, 5, 8, 9, 20, 21, 22], "possibli": [4, 23], "post": [7, 17], "postpon": 22, "power": [1, 23], "practic": [0, 5, 9, 15, 16, 18, 21], "pre": 19, "precis": 5, "prefer": [9, 20], "prevent": 23, "preview": 20, "previou": [1, 4, 5, 19, 21], "previous": [5, 7, 19], "print": [2, 7, 15, 19], "probabl": [4, 7, 17, 23], "problem": [1, 4, 5, 15, 21], "process": [1, 15, 19], "process_data": 15, "product": 1, "profil": 9, "program": 19, "progress": [5, 7], "proident": 7, "project": [2, 4, 5, 7, 9, 10, 11, 15, 17, 18, 19, 21], "properti": [15, 17], "propos": [4, 5], "protect": 22, "provid": [7, 20, 22], "public": [2, 23], "publicli": 23, "publish": [5, 9], "publish_branch": 7, "publish_dir": 7, "pull": [2, 17, 21, 22, 23], "pull_request": [7, 17], "pure": [14, 15], "purpos": [4, 7, 17, 19], "push": [1, 2, 7, 17, 23], "put": 5, "px": 8, "py": [7, 8, 12, 15, 17, 20, 21, 23], "pyc": 22, "pyflam": 12, "pyinstrument": 12, "pypi": [9, 13], "pyplot": 15, "pytest": [10, 11], "python": [8, 10, 13, 15, 16, 17], "python3": 11, "pz": 8, "q": 9, "qualiti": [1, 9, 15], "quarto": 21, "question": [15, 20, 21], "qui": 7, "quick": 19, "quickstart": 7, "quit": [1, 23], "r": [7, 11, 17], "ran": 23, "random": 8, "rang": 1, "rather": [7, 22], "read": [2, 4, 5, 8, 15], "readi": 5, "readm": 23, "readthedoc": 7, "real": [17, 20, 23], "realli": [22, 23], "reason": [4, 20, 22], "rebas": [0, 9], "rebuild": 7, "receiv": [5, 9], "recip": 4, "recogn": [1, 5, 21], "recommend": [5, 7, 9, 10, 19, 20], "record": [9, 19], "recov": 22, "recoveri": 23, "red": 23, "redirect": [20, 23], "reduc": 15, "ref": [4, 7], "refactor": [9, 15], "refer": [2, 4, 5, 7, 17, 22, 23], "refresh": 7, "regard": 7, "region": 15, "regionprop": 15, "regist": 5, "regress": 17, "reject": 4, "rel": 15, "releas": [2, 7, 9, 17, 19, 22], "relev": [2, 22], "reload": 23, "rememb": [1, 20, 21, 22, 23], "remot": [2, 4, 5, 23], "remov": 23, "renam": 23, "render": 7, "repair": 22, "repeat": [5, 19], "replac": [4, 19, 23], "repo": [9, 18], "report": 17, "repositori": [0, 1, 7, 9, 15, 19, 21, 22, 23], "reprehenderit": 7, "repres": [15, 23], "reproduc": [7, 21], "request": [2, 17, 21, 22], "requir": [1, 4, 5, 7, 9, 11, 19, 20], "research": [1, 5, 6], "resid": 2, "resolut": [0, 9], "resolv": 5, "respons": 5, "restart": 23, "restrict": 10, "result": [1, 4, 8, 15, 17], "return": [7, 15, 17], "reus": [6, 23], "reusabl": [8, 9], "review": [4, 7, 9, 17, 21, 22], "reviv": 17, "rewrit": 17, "rgb2grai": 15, "rgb_imag": 15, "right": [4, 5, 7, 17, 19, 20, 23], "risk": 22, "rmd": 7, "robust": 9, "rst": 7, "rstudio": [7, 23], "rtd": 7, "ruff": 15, "run": [4, 7, 10, 15, 17, 20, 23], "rw": 23, "rx": 15, "safe": 22, "safeguard": 22, "safer": 11, "sai": [5, 19, 20], "same": [0, 1, 2, 7, 9, 19, 20, 21, 22, 23], "sandbox": 13, "save": [7, 22], "savefig": 15, "saw": [19, 20], "scale": 8, "scalen": [10, 11, 12], "scari": 17, "scikit": [10, 11], "scipi": 22, "screen": [15, 20], "screenshot": 23, "script": [8, 9, 17, 21], "scroll": 5, "seaborn": [10, 11], "search": [7, 21], "second": 22, "secret": 7, "section": [4, 7], "sed": 7, "see": [1, 4, 5, 7, 10, 17, 19, 20, 23], "seem": 19, "seen": 9, "select": [1, 2, 7, 20, 23], "selector": 19, "selv": 8, "send": [21, 23], "sens": 19, "sensit": 19, "separ": [8, 19], "septemb": 21, "server": [2, 7], "servic": [4, 17, 23], "session": 20, "set": [4, 5, 9, 23], "setup": [7, 17, 20, 23], "sever": 19, "sh": 17, "share": [5, 9, 15, 18, 19, 21], "shell": [7, 10, 15, 17], "short": [4, 17, 19, 23], "shortcut": 20, "shortli": 20, "should": [4, 5, 7, 19, 20, 23], "show": [15, 19, 20], "side": [5, 19], "sidebar": [19, 20], "sigma": 15, "sign": [1, 19], "signal": 5, "signific": 19, "silli": 4, "similar": [19, 20, 21, 23], "simpl": [8, 15, 17, 20, 22, 23], "simpler": 4, "simplifi": 22, "simul": [9, 17, 20, 21], "sinc": [5, 7, 20, 21, 23], "singl": [1, 19], "sint": 7, "sit": 7, "site": 7, "situat": 2, "size": [10, 22], "skimag": 15, "skip": [5, 9], "slightli": 20, "small": [1, 10, 15, 22, 23], "smaller": [2, 22], "snakefil": 15, "snakemak": [9, 10], "snapshot": [2, 17, 19], "so": [1, 4, 5, 19, 20, 22, 23], "softwar": [4, 6, 10, 11, 13, 15, 21], "solut": [1, 4], "solv": [1, 15], "some": [1, 4, 5, 7, 9, 12, 19, 20, 21, 22, 23], "somebodi": [1, 4, 5, 17, 21], "someon": [5, 22], "someth": [1, 4, 5, 7, 17, 21, 22], "sometim": 22, "somewhat": 20, "somewher": 21, "soon": 2, "sound": 21, "sourc": [1, 4, 7, 10, 11, 19, 20, 21, 23], "space": 19, "specif": [1, 17, 19], "specifi": [5, 8], "sphinx": [10, 11], "sphinx_rtd_them": [7, 10, 11], "split": 22, "spy": 12, "ssh": [9, 20, 23], "stabl": 8, "stai": 2, "standard": 20, "star": 15, "star_posit": 15, "start": [4, 5, 7, 8, 19, 20, 21, 22, 23], "stash": 22, "state": 19, "statu": 19, "step": [1, 4, 5, 7, 8, 9, 15, 17, 19, 20, 22, 23], "stick": 19, "sticki": 19, "still": [17, 19, 23], "store": 20, "strategi": [0, 9], "strongli": 1, "structur": [15, 17], "studi": [8, 9], "style": 22, "submit": [2, 4, 5], "submitt": 1, "success": 22, "successfulli": 5, "suggest": [2, 4, 5, 15, 21], "suitabl": [5, 21], "summar": [6, 22], "sunt": 7, "supercomput": 2, "support": 19, "sure": [4, 5, 9, 10, 19, 21, 22, 23], "switch": 23, "symbol": [1, 20, 23], "sync": 4, "syrupi": 17, "system": [8, 21, 23], "systemat": 7, "t": [1, 5, 7, 9, 19, 20, 23], "t_k": 7, "tab": [1, 5, 7, 19, 20], "tag": [2, 22], "take": [2, 4, 20, 23], "target": 15, "task": [1, 4, 5, 19], "team": [2, 5], "technic": [1, 4, 5, 7], "techniqu": 8, "telescop": 15, "tell": [4, 17], "temp_c": 7, "temp_k": 7, "temperatur": 7, "templat": 22, "tempor": 7, "temporari": 19, "term": 20, "termin": [9, 10, 19, 20, 23], "test": [7, 9, 10, 11, 13, 21], "test_": 17, "test_add": 17, "test_force_between_planet": 17, "text": [9, 21], "than": [10, 20, 21, 22, 23], "thank": 5, "thei": [5, 19, 20, 21, 22], "them": [1, 4, 5, 9, 17, 19, 21], "theme": 7, "themselv": 2, "therefor": 9, "thesi": 21, "thi": [1, 4, 5, 6, 7, 8, 9, 10, 11, 17, 19, 21, 22, 23], "thing": [5, 19, 20, 22], "think": 5, "those": 23, "thread": 23, "three": [7, 19, 20, 23], "thresh": 15, "threshold": [7, 15], "threshold_otsu": 15, "through": [1, 4, 5, 9, 15], "thumb": 7, "time": [2, 4, 7, 8, 17, 19, 20, 21, 23], "timeit": 12, "timelin": 20, "tini": [1, 7], "titl": 5, "toctre": 7, "togeth": [1, 2, 9, 12, 15, 19, 20, 22], "too": [7, 20, 22], "tool": [7, 9, 15, 20, 21, 23], "top": [5, 7, 19, 20, 23], "topic": [14, 15], "total": 23, "toward": [1, 9, 17, 20], "track": [5, 9, 19, 20, 22, 23], "tradit": 11, "trajectori": 8, "translat": 17, "travers": 17, "tri": [21, 23], "trick": 19, "troubleshoot": [5, 23], "true": [7, 15, 17], "truli": 23, "trust": [9, 20], "try": [4, 5, 7, 9, 17, 19, 20, 21, 23], "tune": 2, "tupl": 15, "turn": [9, 18, 21], "tutori": [7, 19], "two": [1, 4, 7, 9, 20, 21, 23], "txt": [9, 11, 15], "type": [5, 15, 20], "typic": [2, 9, 22], "typo": 1, "typographi": 7, "u": [5, 17, 19, 20, 23], "ubuntu": [7, 17], "ugli": 22, "ullamco": 7, "under": [2, 15, 20, 23], "understand": [1, 8, 19, 20], "understood": 22, "undo": 22, "unfinish": [22, 23], "unfortun": 20, "uniqu": [2, 19, 20], "unit": [8, 22], "univers": 21, "unless": 23, "unrel": 22, "unsur": [4, 19, 22], "untest": 22, "until": 5, "unwatch": 5, "up": [4, 5, 9, 15, 22, 23], "updat": [1, 2, 22], "upload": 23, "upper": 19, "upstream": [5, 20], "url": [4, 5, 19, 20, 23], "us": [1, 2, 4, 5, 7, 10, 11, 13, 15, 17, 19, 21, 23], "usag": [9, 20], "user": [4, 5, 19, 20, 23], "usernam": [4, 5, 19, 20], "usual": 20, "ut": 7, "uv": 11, "v": [5, 9, 14, 19, 23], "v1": [7, 17, 19], "v4": [7, 17], "valu": [5, 8, 15], "variabl": 20, "ve": 5, "velit": 7, "veloc": 8, "veniam": 7, "venv": 11, "verbos": [4, 5], "veri": [1, 4, 5, 7, 19, 20, 22, 23], "verifi": [4, 5, 23], "version": [2, 7, 9, 17, 19, 20, 23], "version1": 19, "version2": 19, "via": [5, 9, 23], "view": [1, 4, 5, 7, 19, 20], "virtual": [7, 9], "visibl": [5, 20], "visit": 7, "viztrac": 12, "volupt": 7, "vtune": 12, "vultur": [10, 11, 15], "vx": 8, "vy": 8, "vz": 8, "w": [7, 15], "wa": [4, 5, 6, 7, 20, 21, 22], "wai": [1, 4, 5, 7, 15, 17, 19, 20, 22], "walk": [4, 5], "want": [1, 4, 5, 7, 15, 17, 19, 20, 21, 23], "warm": 7, "watch": 5, "we": [1, 2, 4, 5, 7, 10, 12, 13, 15, 17, 19, 20, 22, 23], "web": [1, 5, 7, 23], "websit": [7, 21, 23], "welcom": 9, "were": [5, 8], "what": [1, 4, 6, 8, 9, 10, 19, 20, 23], "whatthecommit": 22, "wheel": 7, "when": [2, 4, 5, 7, 10, 17, 19, 21, 22], "where": [2, 4, 5, 10, 11, 19, 20, 21, 22], "wherea": 2, "whether": [2, 4, 8, 17], "which": [1, 2, 4, 5, 7, 9, 17, 19, 21, 22, 23], "white": 15, "who": [4, 5], "whole": [1, 2, 17, 20], "why": [4, 22], "window": [5, 20], "wish": [4, 5, 15], "within": [0, 2, 9], "without": [1, 4, 5, 7, 20, 21, 23], "wonder": 4, "word": [17, 21], "work": [1, 2, 4, 5, 7, 8, 9, 17, 19, 21, 23], "workflow": [4, 7, 9, 17], "workshop": [4, 5, 6, 7, 9, 15, 17, 20, 21], "would": [2, 4, 15, 17, 20, 22], "write": [2, 4, 5, 7, 14, 15, 23], "written": 8, "wrong": 5, "wrote": 9, "x": 15, "y": 15, "ye": [20, 23], "year": 22, "yet": [5, 17, 19, 23], "yml": [7, 9, 10, 17], "you": [1, 2, 4, 5, 9, 17, 21, 22, 23], "your": [1, 2, 5, 8, 9, 10, 14, 17, 18, 19, 20, 22], "yourself": 23, "zenodo": [9, 13, 23], "zip": 21, "zola": 7, "zombi": 7, "zsh": 10}, "titles": ["Collaborative version control and code review", "Practicing code review", "Concepts around collaboration", "Conclict resolution, rebasing, and organizational strategies", "How to contribute changes to repositories that belong to others", "Collaborating within the same repository", "Credit", "Code documentation", "Example project: Simulating the motion of planets", "Reproducible research software development using Python", "Conda environment", "Virtual environment", "Profiling memory and CPU usage", "How to release and publish your code", "Concepts in refactoring and modular code design", "Demo: From a script towards a workflow", "How to make the project more reusable", "Automated testing", "Introduction to version control with Git and GitHub", "Creating branches and commits", "Forking, cloning, and browsing", "Motivation", "Practical advice: How much Git is necessary?", "Optional: How to turn your project to a Git repo and share it"], "titleterms": {"": 4, "1": [5, 9, 19, 20], "2": [5, 9, 19, 20], "20": [19, 20, 23], "25": [1, 4, 5], "3": [5, 9, 19, 20], "4": [5, 19, 20], "5": [5, 19, 20], "6": [5, 19, 20], "7": [5, 19], "8": 19, "And": 5, "If": 9, "In": 7, "about": [7, 21, 22], "action": [7, 17], "ad": [5, 17], "address": 1, "advic": 22, "after": 15, "again": 19, "alert": 15, "all": 22, "allow": [7, 20], "an": [5, 20], "answer": 17, "api": 7, "approach": 4, "ar": 20, "arbitrari": 19, "around": 2, "ask": 1, "auto": 7, "autom": [4, 17], "avoid": 22, "back": 21, "background": 19, "basic": 20, "befor": [10, 11], "belong": 4, "between": 2, "big": 9, "branch": [2, 5, 19, 21, 22], "brows": [19, 20], "build": 7, "can": [4, 20], "chang": [1, 2, 4, 5], "checklist": 7, "choos": 10, "clone": [2, 20], "code": [0, 1, 7, 13, 14, 20, 21], "collabor": [0, 2, 4, 5, 21], "command": [15, 20, 22], "comment": 1, "commit": [2, 19, 20, 22], "compar": [19, 20], "complex": 22, "concept": [2, 14], "conclict": 3, "conda": 10, "confus": 7, "contribut": 4, "control": [0, 18], "copi": 20, "coupl": 4, "cpu": 12, "creat": [5, 10, 11, 19, 20], "credit": 6, "cross": 5, "dai": 9, "defin": 15, "demo": [7, 15], "demonstr": 21, "design": 14, "develop": 9, "discuss": [1, 4, 17, 21], "di\u00e1taxi": 7, "do": 21, "docstr": 7, "document": 7, "don": 4, "draft": 5, "each": [20, 22], "els": 17, "end": 17, "enough": 7, "environ": [10, 11], "exampl": 8, "exercis": [1, 4, 5, 7, 17, 19, 20, 23], "exist": 20, "featur": 21, "file": [19, 20], "focu": 8, "fork": [2, 4, 20], "from": [4, 15], "futur": 7, "gener": 7, "git": [18, 22, 23], "github": [5, 7, 17, 18, 20, 23], "gitlab": 23, "goal": [8, 9], "graph": 20, "have": [4, 9], "help": [1, 4], "hint": 5, "histori": 20, "how": [1, 4, 5, 7, 13, 16, 17, 20, 22, 23], "i": [4, 5, 7, 17, 22, 23], "idea": 4, "includ": 20, "instantan": 20, "instructor": [7, 8, 17, 20], "interfac": 15, "introduct": 18, "issu": 5, "just": 19, "keep": 21, "keypoint": 7, "languag": 7, "larg": 22, "last": 20, "learn": 8, "left": 9, "level": 22, "like": 21, "line": [15, 20, 22], "local": 4, "longer": 17, "made": 19, "main": [5, 19], "make": [5, 16], "manag": 10, "mani": 7, "markdown": 7, "memori": 12, "merg": 21, "messag": 22, "min": [1, 4, 5, 19, 20, 23], "modif": 20, "modifi": [1, 5, 19, 20], "modular": 14, "more": [7, 16], "motion": 8, "motiv": [4, 17, 21], "much": 22, "my": 4, "myst": 7, "necessari": 22, "need": [7, 21], "network": 20, "new": [5, 19], "note": [7, 8, 17, 20], "object": [2, 7, 17, 19, 20, 21, 23], "often": [7, 17], "open": [4, 5], "option": [7, 23], "organiz": 3, "other": 4, "peopl": 4, "permiss": 4, "pictur": 9, "plan": 15, "planet": 8, "point": 15, "posit": 20, "possibl": [15, 17], "practic": [1, 19, 22], "prepar": [1, 4, 5, 7, 9, 20], "prerequisit": 9, "profil": 12, "project": [8, 16, 20, 22, 23], "protect": 5, "publish": [13, 23], "pull": [1, 4, 5], "push": [4, 5], "put": 23, "pytest": 17, "python": [7, 9], "read": 7, "readm": 7, "rebas": 3, "recip": 5, "recoveri": 4, "refactor": 14, "referenc": 5, "releas": 13, "renam": 19, "repo": 23, "repositori": [2, 4, 5, 20], "reproduc": 9, "request": [1, 4, 5], "research": 9, "resolut": 3, "restructuredtext": 7, "reusabl": 16, "review": [0, 1, 5], "roll": 21, "rough": 15, "rule": 15, "run": 8, "same": [4, 5], "schedul": 9, "script": 15, "search": 20, "self": 7, "set": 7, "share": [20, 23], "should": 22, "simul": 8, "singl": 20, "snakemak": 15, "snapshot": 21, "softwar": [9, 23], "solut": [5, 15, 19, 20, 21], "some": 15, "sphinx": 7, "spoiler": 15, "stage": 22, "start": [15, 17], "statu": 22, "strategi": 3, "suggest": 1, "summari": [1, 4, 5, 19, 20], "switch": 19, "synchron": 2, "t": 4, "tag": 19, "talk": 21, "taught": 17, "test": [4, 17], "than": 7, "thi": [15, 20], "through": [19, 20], "time": [9, 22], "tool": 10, "toward": [4, 5, 15], "track": 21, "turn": 23, "two": 19, "typic": 21, "unit": 17, "up": 7, "updat": 4, "upstream": 4, "us": [9, 20, 22], "usag": 12, "v": [7, 20], "version": [0, 18, 21], "virtual": [10, 11], "walk": [19, 20], "we": [8, 9, 21], "what": [5, 7, 17, 21, 22], "when": 20, "where": [7, 17], "which": [15, 20], "who": 20, "why": [7, 21], "within": [4, 5], "word": 20, "work": [15, 20, 22], "workflow": 15, "write": 22, "you": [7, 10, 11, 19, 20], "your": [4, 7, 13, 23], "yourself": 20}}) \ No newline at end of file diff --git a/singlehtml/_images/add-suggestion.png b/singlehtml/_images/add-suggestion.png new file mode 100644 index 0000000..0a28544 Binary files /dev/null and b/singlehtml/_images/add-suggestion.png differ diff --git a/singlehtml/_images/all-checks-failed.png b/singlehtml/_images/all-checks-failed.png new file mode 100644 index 0000000..5175684 Binary files /dev/null and b/singlehtml/_images/all-checks-failed.png differ diff --git a/singlehtml/_images/animation.mp4 b/singlehtml/_images/animation.mp4 new file mode 100644 index 0000000..f037fd4 Binary files /dev/null and b/singlehtml/_images/animation.mp4 differ diff --git a/singlehtml/_images/bare-repository.png b/singlehtml/_images/bare-repository.png new file mode 100644 index 0000000..45286f9 Binary files /dev/null and b/singlehtml/_images/bare-repository.png differ diff --git a/singlehtml/_images/branches.png b/singlehtml/_images/branches.png new file mode 100644 index 0000000..2464b1c Binary files /dev/null and b/singlehtml/_images/branches.png differ diff --git a/singlehtml/_images/check-details.png b/singlehtml/_images/check-details.png new file mode 100644 index 0000000..456a247 Binary files /dev/null and b/singlehtml/_images/check-details.png differ diff --git a/singlehtml/_images/clone-address.png b/singlehtml/_images/clone-address.png new file mode 100644 index 0000000..abdbd85 Binary files /dev/null and b/singlehtml/_images/clone-address.png differ diff --git a/singlehtml/_images/clone-of-fork.png b/singlehtml/_images/clone-of-fork.png new file mode 100644 index 0000000..de1f78d Binary files /dev/null and b/singlehtml/_images/clone-of-fork.png differ diff --git a/singlehtml/_images/clone.png b/singlehtml/_images/clone.png new file mode 100644 index 0000000..ad948ba Binary files /dev/null and b/singlehtml/_images/clone.png differ diff --git a/singlehtml/_images/comment.png b/singlehtml/_images/comment.png new file mode 100644 index 0000000..6ae948c Binary files /dev/null and b/singlehtml/_images/comment.png differ diff --git a/singlehtml/_images/commit-suggestion.png b/singlehtml/_images/commit-suggestion.png new file mode 100644 index 0000000..4ec4bb6 Binary files /dev/null and b/singlehtml/_images/commit-suggestion.png differ diff --git a/singlehtml/_images/create-repository-with-readme.png b/singlehtml/_images/create-repository-with-readme.png new file mode 100644 index 0000000..2411834 Binary files /dev/null and b/singlehtml/_images/create-repository-with-readme.png differ diff --git a/singlehtml/_images/create-repository.png b/singlehtml/_images/create-repository.png new file mode 100644 index 0000000..80fb99c Binary files /dev/null and b/singlehtml/_images/create-repository.png differ diff --git a/singlehtml/_images/draft-pr-wip.png b/singlehtml/_images/draft-pr-wip.png new file mode 100644 index 0000000..12181d5 Binary files /dev/null and b/singlehtml/_images/draft-pr-wip.png differ diff --git a/singlehtml/_images/draft-pr.png b/singlehtml/_images/draft-pr.png new file mode 100644 index 0000000..2099349 Binary files /dev/null and b/singlehtml/_images/draft-pr.png differ diff --git a/singlehtml/_images/files-changed.png b/singlehtml/_images/files-changed.png new file mode 100644 index 0000000..7b58a22 Binary files /dev/null and b/singlehtml/_images/files-changed.png differ diff --git a/singlehtml/_images/fork-after-update.png b/singlehtml/_images/fork-after-update.png new file mode 100644 index 0000000..38fc8c6 Binary files /dev/null and b/singlehtml/_images/fork-after-update.png differ diff --git a/singlehtml/_images/fork.png b/singlehtml/_images/fork.png new file mode 100644 index 0000000..e658473 Binary files /dev/null and b/singlehtml/_images/fork.png differ diff --git a/singlehtml/_images/forkandclone.png b/singlehtml/_images/forkandclone.png new file mode 100644 index 0000000..d050183 Binary files /dev/null and b/singlehtml/_images/forkandclone.png differ diff --git a/singlehtml/_images/gophers.png b/singlehtml/_images/gophers.png new file mode 100644 index 0000000..741406b Binary files /dev/null and b/singlehtml/_images/gophers.png differ diff --git a/singlehtml/_images/history.png b/singlehtml/_images/history.png new file mode 100644 index 0000000..a3a0d99 Binary files /dev/null and b/singlehtml/_images/history.png differ diff --git a/singlehtml/_images/issue-number.png b/singlehtml/_images/issue-number.png new file mode 100644 index 0000000..be273a2 Binary files /dev/null and b/singlehtml/_images/issue-number.png differ diff --git a/singlehtml/_images/leave-comment.png b/singlehtml/_images/leave-comment.png new file mode 100644 index 0000000..95e33c2 Binary files /dev/null and b/singlehtml/_images/leave-comment.png differ diff --git a/singlehtml/_images/license.png b/singlehtml/_images/license.png new file mode 100644 index 0000000..ec5cf85 Binary files /dev/null and b/singlehtml/_images/license.png differ diff --git a/singlehtml/_images/network.png b/singlehtml/_images/network.png new file mode 100644 index 0000000..26a6d63 Binary files /dev/null and b/singlehtml/_images/network.png differ diff --git a/singlehtml/_images/new-repository.png b/singlehtml/_images/new-repository.png new file mode 100644 index 0000000..aff7677 Binary files /dev/null and b/singlehtml/_images/new-repository.png differ diff --git a/singlehtml/_images/owl.png b/singlehtml/_images/owl.png new file mode 100644 index 0000000..7f90e9f Binary files /dev/null and b/singlehtml/_images/owl.png differ diff --git a/singlehtml/_images/pull-request-form.png b/singlehtml/_images/pull-request-form.png new file mode 100644 index 0000000..c6c6af8 Binary files /dev/null and b/singlehtml/_images/pull-request-form.png differ diff --git a/singlehtml/_images/sharing.png b/singlehtml/_images/sharing.png new file mode 100644 index 0000000..8368d6b Binary files /dev/null and b/singlehtml/_images/sharing.png differ diff --git a/singlehtml/_images/stars.png b/singlehtml/_images/stars.png new file mode 100644 index 0000000..f4c5cf7 Binary files /dev/null and b/singlehtml/_images/stars.png differ diff --git a/singlehtml/_images/sync-fork.png b/singlehtml/_images/sync-fork.png new file mode 100644 index 0000000..848a054 Binary files /dev/null and b/singlehtml/_images/sync-fork.png differ diff --git a/singlehtml/_images/unwatch.png b/singlehtml/_images/unwatch.png new file mode 100644 index 0000000..020a6c9 Binary files /dev/null and b/singlehtml/_images/unwatch.png differ diff --git a/singlehtml/_images/upload-files.png b/singlehtml/_images/upload-files.png new file mode 100644 index 0000000..fd3ef8c Binary files /dev/null and b/singlehtml/_images/upload-files.png differ diff --git a/singlehtml/_images/vscode-authorize.png b/singlehtml/_images/vscode-authorize.png new file mode 100644 index 0000000..4d45dc2 Binary files /dev/null and b/singlehtml/_images/vscode-authorize.png differ diff --git a/singlehtml/_images/vscode-publish-branch.png b/singlehtml/_images/vscode-publish-branch.png new file mode 100644 index 0000000..323ffb2 Binary files /dev/null and b/singlehtml/_images/vscode-publish-branch.png differ diff --git a/singlehtml/_images/vscode-publish-to-github1.png b/singlehtml/_images/vscode-publish-to-github1.png new file mode 100644 index 0000000..883e756 Binary files /dev/null and b/singlehtml/_images/vscode-publish-to-github1.png differ diff --git a/singlehtml/_images/vscode-publish-to-github2.png b/singlehtml/_images/vscode-publish-to-github2.png new file mode 100644 index 0000000..d0055db Binary files /dev/null and b/singlehtml/_images/vscode-publish-to-github2.png differ diff --git a/singlehtml/_images/vscode-publish-to-github3.png b/singlehtml/_images/vscode-publish-to-github3.png new file mode 100644 index 0000000..0f68d0e Binary files /dev/null and b/singlehtml/_images/vscode-publish-to-github3.png differ diff --git a/singlehtml/_images/vscode-start.png b/singlehtml/_images/vscode-start.png new file mode 100644 index 0000000..e783080 Binary files /dev/null and b/singlehtml/_images/vscode-start.png differ diff --git a/singlehtml/_static/_sphinx_javascript_frameworks_compat.js b/singlehtml/_static/_sphinx_javascript_frameworks_compat.js new file mode 100644 index 0000000..8141580 --- /dev/null +++ b/singlehtml/_static/_sphinx_javascript_frameworks_compat.js @@ -0,0 +1,123 @@ +/* Compatability shim for jQuery and underscores.js. + * + * Copyright Sphinx contributors + * Released under the two clause BSD licence + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} diff --git a/singlehtml/_static/basic.css b/singlehtml/_static/basic.css new file mode 100644 index 0000000..f316efc --- /dev/null +++ b/singlehtml/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/singlehtml/_static/check-solid.svg b/singlehtml/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/singlehtml/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/singlehtml/_static/clipboard.min.js b/singlehtml/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/singlehtml/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/singlehtml/_static/copybutton.css b/singlehtml/_static/copybutton.css new file mode 100644 index 0000000..40eafe5 --- /dev/null +++ b/singlehtml/_static/copybutton.css @@ -0,0 +1,93 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +.highlight:hover button.copybtn { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/singlehtml/_static/copybutton.js b/singlehtml/_static/copybutton.js new file mode 100644 index 0000000..f3ecd03 --- /dev/null +++ b/singlehtml/_static/copybutton.js @@ -0,0 +1,241 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copié dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for two seconds, then changes it back +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + setTimeout(() => el.setAttribute('data-tooltip', oldText), 2000) + setTimeout(() => el.classList.remove('success'), 2000) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, 2000) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos, .gp'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/singlehtml/_static/copybutton_funcs.js b/singlehtml/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/singlehtml/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/singlehtml/_static/css/badge_only.css b/singlehtml/_static/css/badge_only.css new file mode 100644 index 0000000..c718cee --- /dev/null +++ b/singlehtml/_static/css/badge_only.css @@ -0,0 +1 @@ +.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} \ No newline at end of file diff --git a/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff b/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff2 b/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/singlehtml/_static/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff b/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff2 b/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/singlehtml/_static/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/singlehtml/_static/css/fonts/fontawesome-webfont.eot b/singlehtml/_static/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/singlehtml/_static/css/fonts/fontawesome-webfont.eot differ diff --git a/singlehtml/_static/css/fonts/fontawesome-webfont.svg b/singlehtml/_static/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/singlehtml/_static/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/singlehtml/_static/css/fonts/fontawesome-webfont.ttf b/singlehtml/_static/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/singlehtml/_static/css/fonts/fontawesome-webfont.ttf differ diff --git a/singlehtml/_static/css/fonts/fontawesome-webfont.woff b/singlehtml/_static/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/singlehtml/_static/css/fonts/fontawesome-webfont.woff differ diff --git a/singlehtml/_static/css/fonts/fontawesome-webfont.woff2 b/singlehtml/_static/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/singlehtml/_static/css/fonts/fontawesome-webfont.woff2 differ diff --git a/singlehtml/_static/css/fonts/lato-bold-italic.woff b/singlehtml/_static/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-bold-italic.woff differ diff --git a/singlehtml/_static/css/fonts/lato-bold-italic.woff2 b/singlehtml/_static/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-bold-italic.woff2 differ diff --git a/singlehtml/_static/css/fonts/lato-bold.woff b/singlehtml/_static/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-bold.woff differ diff --git a/singlehtml/_static/css/fonts/lato-bold.woff2 b/singlehtml/_static/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-bold.woff2 differ diff --git a/singlehtml/_static/css/fonts/lato-normal-italic.woff b/singlehtml/_static/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-normal-italic.woff differ diff --git a/singlehtml/_static/css/fonts/lato-normal-italic.woff2 b/singlehtml/_static/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-normal-italic.woff2 differ diff --git a/singlehtml/_static/css/fonts/lato-normal.woff b/singlehtml/_static/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-normal.woff differ diff --git a/singlehtml/_static/css/fonts/lato-normal.woff2 b/singlehtml/_static/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/singlehtml/_static/css/fonts/lato-normal.woff2 differ diff --git a/singlehtml/_static/css/theme.css b/singlehtml/_static/css/theme.css new file mode 100644 index 0000000..19a446a --- /dev/null +++ b/singlehtml/_static/css/theme.css @@ -0,0 +1,4 @@ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel,.rst-content .menuselection{font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content .guilabel,.rst-content .menuselection{border:1px solid #7fbbe3;background:#e7f2fa}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} \ No newline at end of file diff --git a/singlehtml/_static/doctools.js b/singlehtml/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/singlehtml/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/singlehtml/_static/documentation_options.js b/singlehtml/_static/documentation_options.js new file mode 100644 index 0000000..89003c6 --- /dev/null +++ b/singlehtml/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'singlehtml', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/singlehtml/_static/file.png b/singlehtml/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/singlehtml/_static/file.png differ diff --git a/singlehtml/_static/jquery.js b/singlehtml/_static/jquery.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/singlehtml/_static/jquery.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/singlehtml/_static/js/html5shiv.min.js b/singlehtml/_static/js/html5shiv.min.js new file mode 100644 index 0000000..cd1c674 --- /dev/null +++ b/singlehtml/_static/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); \ No newline at end of file diff --git a/singlehtml/_static/js/theme.js b/singlehtml/_static/js/theme.js new file mode 100644 index 0000000..1fddb6e --- /dev/null +++ b/singlehtml/_static/js/theme.js @@ -0,0 +1 @@ +!function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/singlehtml/_static/minipres.js b/singlehtml/_static/minipres.js new file mode 100644 index 0000000..ad11c87 --- /dev/null +++ b/singlehtml/_static/minipres.js @@ -0,0 +1,223 @@ +// Add goTo method to elements +// http://stackoverflow.com/questions/4801655/how-to-go-to-a-specific-element-on-page +(function($) { + $.fn.goTo = function() { + $('html, body').animate({ + scrollTop: $(this).offset().top //+ 'px' + }, 'fast'); + return this; // for chaining... + } +})(jQuery); + +// NO good way to do this!. Copy a hack from here +// https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript +// https://stackoverflow.com/a/2880929 +var urlParams; +(window.onpopstate = function () { + var match, + pl = /\+/g, // Regex for replacing addition symbol with a space + search = /([^&=]+)=?([^&]*)/g, + decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, + query = window.location.search.substring(1); + urlParams = {}; + while (match = search.exec(query)) + urlParams[decode(match[1])] = decode(match[2]); +})(); + +// Select heading levels +var maxHeading = urlParams['h'] +if (maxHeading === undefined) maxHeading = 2 +var headingLevels = []; +for (h=2 ; h (sections.length-1) ) { + // if we would scroll past bottom, or above top, do nothing + return; + } + + console.log('xxxxxx'); + var targetSection = sections[targetPos]; + console.log(targetSection, typeof(targetSection)); + + // Return targetSection top and height + var secProperties = section_top_and_height(targetSection); + var top = secProperties['top']; + var height = secProperties['height'] + var win_height = window.innerHeight; + //console.info(top, height, win_height) + + var scroll_to = 0; + if (height >= win_height || height == 0) { + scroll_to = top; + } else { + scroll_to = top - (win_height-height)/3.; + } + //console.info(top, height, win_height, scroll_to) + + $('html, body').animate({ + scrollTop: scroll_to //+ 'px' + }, 'fast'); + +} + + +function minipres() { + /* Enable the minipres mode: + - call the hide() function + - set up the scrolling listener + */ + document.addEventListener('keydown', function (event) { + switch(event.which) { + case 37: // left + switch_slide(-1); + event.preventDefault(); + return false; + break; + //case 38: // up + case 39: // right + switch_slide(+1); + event.preventDefault(); + return false; + break; + //case 40: // down + default: + return; // exit this handler for other keys + } + }, true) + + hide() + + // Increase space between sections + //$("div .section").css('margin-bottom', '50%'); + $(sectionSelector).css('margin-top', '50%'); + + // Reduce size/color of other sections + if (hiddenSectionSelector.length > 0) { + var hideNodes = $(hiddenSectionSelector); + console.log(typeof hideNodes, hideNodes); + for (node in hideNodes) { + console.log("a", typeof node, node); + node = hideNodes[node]; // what's right way to iterate values? + console.log("b", typeof node, node); + if (node.parentNode && node.parentNode.className == "section") { + node = node.parentNode; + console.log("c", typeof node, node); + //node.css['transform'] = 'scale(.5)'; + //node.css['transform-origin'] = 'top center'; + $(node).css('color', 'lightgrey'); + //$(node).css('font-size', '20%'); + //$(node).css('visibility', 'collapse'); + //ntahousnatouhasno; + } + } + } +} + +function hide() { + /* Hide all non-essential elements on the page + */ + + // This is for sphinx_rst_theme and readthedocs + $(".wy-nav-side").remove(); + $(".wy-nav-content-wrap").css('margin-left', 0); + $('.rst-versions').remove(); // readthedocs version selector + + // Add other formats here. +} + + +var slideshow = minipres; + +if (window.location.search.match(/[?&](minipres|slideshow|pres)([=&]|$)/) ) { + //minipres() + window.addEventListener("load", minipres); +} else if (window.location.search.match(/[?&](plain)([=&]|$)/) ) { + window.addEventListener("load", hide); +} diff --git a/singlehtml/_static/minus.png b/singlehtml/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/singlehtml/_static/minus.png differ diff --git a/singlehtml/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css b/singlehtml/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css new file mode 100644 index 0000000..3356631 --- /dev/null +++ b/singlehtml/_static/mystnb.4510f1fc1dee50b3e5859aac5469c37c29e427902b24a333a5f9fcb2f0b3ac41.css @@ -0,0 +1,2342 @@ +/* Variables */ +:root { + --mystnb-source-bg-color: #f7f7f7; + --mystnb-stdout-bg-color: #fcfcfc; + --mystnb-stderr-bg-color: #fdd; + --mystnb-traceback-bg-color: #fcfcfc; + --mystnb-source-border-color: #ccc; + --mystnb-source-margin-color: green; + --mystnb-stdout-border-color: #f7f7f7; + --mystnb-stderr-border-color: #f7f7f7; + --mystnb-traceback-border-color: #ffd6d6; + --mystnb-hide-prompt-opacity: 70%; + --mystnb-source-border-radius: .4em; + --mystnb-source-border-width: 1px; +} + +/* Whole cell */ +div.container.cell { + padding-left: 0; + margin-bottom: 1em; +} + +/* Removing all background formatting so we can control at the div level */ +.cell_input div.highlight, +.cell_output pre, +.cell_input pre, +.cell_output .output { + border: none; + box-shadow: none; +} + +.cell_output .output pre, +.cell_input pre { + margin: 0px; +} + +/* Input cells */ +div.cell div.cell_input, +div.cell details.above-input>summary { + padding-left: 0em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + background-color: var(--mystnb-source-bg-color); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; + border-radius: var(--mystnb-source-border-radius); +} + +div.cell_input>div, +div.cell_output div.output>div.highlight { + margin: 0em !important; + border: none !important; +} + +/* All cell outputs */ +.cell_output { + padding-left: 1em; + padding-right: 0em; + margin-top: 1em; +} + +/* Text outputs from cells */ +.cell_output .output.text_plain, +.cell_output .output.traceback, +.cell_output .output.stream, +.cell_output .output.stderr { + margin-top: 1em; + margin-bottom: 0em; + box-shadow: none; +} + +.cell_output .output.text_plain, +.cell_output .output.stream { + background: var(--mystnb-stdout-bg-color); + border: 1px solid var(--mystnb-stdout-border-color); +} + +.cell_output .output.stderr { + background: var(--mystnb-stderr-bg-color); + border: 1px solid var(--mystnb-stderr-border-color); +} + +.cell_output .output.traceback { + background: var(--mystnb-traceback-bg-color); + border: 1px solid var(--mystnb-traceback-border-color); +} + +/* Collapsible cell content */ +div.cell details.above-input div.cell_input { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; +} + +div.cell div.cell_input.above-output-prompt { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +div.cell details.above-input>summary { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: var(--mystnb-source-border-width) var(--mystnb-source-border-color) dashed; + padding-left: 1em; + margin-bottom: 0; +} + +div.cell details.above-output>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.below-input>summary { + background-color: var(--mystnb-source-bg-color); + padding-left: 1em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + border-top: none; + border-bottom-left-radius: var(--mystnb-source-border-radius); + border-bottom-right-radius: var(--mystnb-source-border-radius); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; +} + +div.cell details.hide>summary>span { + opacity: var(--mystnb-hide-prompt-opacity); +} + +div.cell details.hide[open]>summary>span.collapsed { + display: none; +} + +div.cell details.hide:not([open])>summary>span.expanded { + display: none; +} + +@keyframes collapsed-fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} +div.cell details.hide[open]>summary~* { + -moz-animation: collapsed-fade-in 0.3s ease-in-out; + -webkit-animation: collapsed-fade-in 0.3s ease-in-out; + animation: collapsed-fade-in 0.3s ease-in-out; +} + +/* Math align to the left */ +.cell_output .MathJax_Display { + text-align: left !important; +} + +/* Pandas tables. Pulled from the Jupyter / nbsphinx CSS */ +div.cell_output table { + border: none; + border-collapse: collapse; + border-spacing: 0; + color: black; + font-size: 1em; + table-layout: fixed; +} + +div.cell_output thead { + border-bottom: 1px solid black; + vertical-align: bottom; +} + +div.cell_output tr, +div.cell_output th, +div.cell_output td { + text-align: right; + vertical-align: middle; + padding: 0.5em 0.5em; + line-height: normal; + white-space: normal; + max-width: none; + border: none; +} + +div.cell_output th { + font-weight: bold; +} + +div.cell_output tbody tr:nth-child(odd) { + background: #f5f5f5; +} + +div.cell_output tbody tr:hover { + background: rgba(66, 165, 245, 0.2); +} + +/** source code line numbers **/ +span.linenos { + opacity: 0.5; +} + +/* Inline text from `paste` operation */ + +span.pasted-text { + font-weight: bold; +} + +span.pasted-inline img { + max-height: 2em; +} + +tbody span.pasted-inline img { + max-height: none; +} + +/* Font colors for translated ANSI escape sequences +Color values are copied from Jupyter Notebook +https://github.com/jupyter/notebook/blob/52581f8eda9b319eb0390ac77fe5903c38f81e3e/notebook/static/notebook/less/ansicolors.less#L14-L21 +Background colors from +https://nbsphinx.readthedocs.io/en/latest/code-cells.html#ANSI-Colors +*/ +div.highlight .-Color-Bold { + font-weight: bold; +} + +div.highlight .-Color[class*=-Black] { + color: #3E424D +} + +div.highlight .-Color[class*=-Red] { + color: #E75C58 +} + +div.highlight .-Color[class*=-Green] { + color: #00A250 +} + +div.highlight .-Color[class*=-Yellow] { + color: #DDB62B +} + +div.highlight .-Color[class*=-Blue] { + color: #208FFB +} + +div.highlight .-Color[class*=-Magenta] { + color: #D160C4 +} + +div.highlight .-Color[class*=-Cyan] { + color: #60C6C8 +} + +div.highlight .-Color[class*=-White] { + color: #C5C1B4 +} + +div.highlight .-Color[class*=-BGBlack] { + background-color: #3E424D +} + +div.highlight .-Color[class*=-BGRed] { + background-color: #E75C58 +} + +div.highlight .-Color[class*=-BGGreen] { + background-color: #00A250 +} + +div.highlight .-Color[class*=-BGYellow] { + background-color: #DDB62B +} + +div.highlight .-Color[class*=-BGBlue] { + background-color: #208FFB +} + +div.highlight .-Color[class*=-BGMagenta] { + background-color: #D160C4 +} + +div.highlight .-Color[class*=-BGCyan] { + background-color: #60C6C8 +} + +div.highlight .-Color[class*=-BGWhite] { + background-color: #C5C1B4 +} + +/* Font colors for 8-bit ANSI */ + +div.highlight .-Color[class*=-C0] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC0] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C1] { + color: #800000 +} + +div.highlight .-Color[class*=-BGC1] { + background-color: #800000 +} + +div.highlight .-Color[class*=-C2] { + color: #008000 +} + +div.highlight .-Color[class*=-BGC2] { + background-color: #008000 +} + +div.highlight .-Color[class*=-C3] { + color: #808000 +} + +div.highlight .-Color[class*=-BGC3] { + background-color: #808000 +} + +div.highlight .-Color[class*=-C4] { + color: #000080 +} + +div.highlight .-Color[class*=-BGC4] { + background-color: #000080 +} + +div.highlight .-Color[class*=-C5] { + color: #800080 +} + +div.highlight .-Color[class*=-BGC5] { + background-color: #800080 +} + +div.highlight .-Color[class*=-C6] { + color: #008080 +} + +div.highlight .-Color[class*=-BGC6] { + background-color: #008080 +} + +div.highlight .-Color[class*=-C7] { + color: #C0C0C0 +} + +div.highlight .-Color[class*=-BGC7] { + background-color: #C0C0C0 +} + +div.highlight .-Color[class*=-C8] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC8] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C9] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC9] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C10] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC10] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C11] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC11] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C12] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC12] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C13] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC13] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C14] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC14] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C15] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC15] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C16] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC16] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C17] { + color: #00005F +} + +div.highlight .-Color[class*=-BGC17] { + background-color: #00005F +} + +div.highlight .-Color[class*=-C18] { + color: #000087 +} + +div.highlight .-Color[class*=-BGC18] { + background-color: #000087 +} + +div.highlight .-Color[class*=-C19] { + color: #0000AF +} + +div.highlight .-Color[class*=-BGC19] { + background-color: #0000AF +} + +div.highlight .-Color[class*=-C20] { + color: #0000D7 +} + +div.highlight .-Color[class*=-BGC20] { + background-color: #0000D7 +} + +div.highlight .-Color[class*=-C21] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC21] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C22] { + color: #005F00 +} + +div.highlight .-Color[class*=-BGC22] { + background-color: #005F00 +} + +div.highlight .-Color[class*=-C23] { + color: #005F5F +} + +div.highlight .-Color[class*=-BGC23] { + background-color: #005F5F +} + +div.highlight .-Color[class*=-C24] { + color: #005F87 +} + +div.highlight .-Color[class*=-BGC24] { + background-color: #005F87 +} + +div.highlight .-Color[class*=-C25] { + color: #005FAF +} + +div.highlight .-Color[class*=-BGC25] { + background-color: #005FAF +} + +div.highlight .-Color[class*=-C26] { + color: #005FD7 +} + +div.highlight .-Color[class*=-BGC26] { + background-color: #005FD7 +} + +div.highlight .-Color[class*=-C27] { + color: #005FFF +} + +div.highlight .-Color[class*=-BGC27] { + background-color: #005FFF +} + +div.highlight .-Color[class*=-C28] { + color: #008700 +} + +div.highlight .-Color[class*=-BGC28] { + background-color: #008700 +} + +div.highlight .-Color[class*=-C29] { + color: #00875F +} + +div.highlight .-Color[class*=-BGC29] { + background-color: #00875F +} + +div.highlight .-Color[class*=-C30] { + color: #008787 +} + +div.highlight .-Color[class*=-BGC30] { + background-color: #008787 +} + +div.highlight .-Color[class*=-C31] { + color: #0087AF +} + +div.highlight .-Color[class*=-BGC31] { + background-color: #0087AF +} + +div.highlight .-Color[class*=-C32] { + color: #0087D7 +} + +div.highlight .-Color[class*=-BGC32] { + background-color: #0087D7 +} + +div.highlight .-Color[class*=-C33] { + color: #0087FF +} + +div.highlight .-Color[class*=-BGC33] { + background-color: #0087FF +} + +div.highlight .-Color[class*=-C34] { + color: #00AF00 +} + +div.highlight .-Color[class*=-BGC34] { + background-color: #00AF00 +} + +div.highlight .-Color[class*=-C35] { + color: #00AF5F +} + +div.highlight .-Color[class*=-BGC35] { + background-color: #00AF5F +} + +div.highlight .-Color[class*=-C36] { + color: #00AF87 +} + +div.highlight .-Color[class*=-BGC36] { + background-color: #00AF87 +} + +div.highlight .-Color[class*=-C37] { + color: #00AFAF +} + +div.highlight .-Color[class*=-BGC37] { + background-color: #00AFAF +} + +div.highlight .-Color[class*=-C38] { + color: #00AFD7 +} + +div.highlight .-Color[class*=-BGC38] { + background-color: #00AFD7 +} + +div.highlight .-Color[class*=-C39] { + color: #00AFFF +} + +div.highlight .-Color[class*=-BGC39] { + background-color: #00AFFF +} + +div.highlight .-Color[class*=-C40] { + color: #00D700 +} + +div.highlight .-Color[class*=-BGC40] { + background-color: #00D700 +} + +div.highlight .-Color[class*=-C41] { + color: #00D75F +} + +div.highlight .-Color[class*=-BGC41] { + background-color: #00D75F +} + +div.highlight .-Color[class*=-C42] { + color: #00D787 +} + +div.highlight .-Color[class*=-BGC42] { + background-color: #00D787 +} + +div.highlight .-Color[class*=-C43] { + color: #00D7AF +} + +div.highlight .-Color[class*=-BGC43] { + background-color: #00D7AF +} + +div.highlight .-Color[class*=-C44] { + color: #00D7D7 +} + +div.highlight .-Color[class*=-BGC44] { + background-color: #00D7D7 +} + +div.highlight .-Color[class*=-C45] { + color: #00D7FF +} + +div.highlight .-Color[class*=-BGC45] { + background-color: #00D7FF +} + +div.highlight .-Color[class*=-C46] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC46] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C47] { + color: #00FF5F +} + +div.highlight .-Color[class*=-BGC47] { + background-color: #00FF5F +} + +div.highlight .-Color[class*=-C48] { + color: #00FF87 +} + +div.highlight .-Color[class*=-BGC48] { + background-color: #00FF87 +} + +div.highlight .-Color[class*=-C49] { + color: #00FFAF +} + +div.highlight .-Color[class*=-BGC49] { + background-color: #00FFAF +} + +div.highlight .-Color[class*=-C50] { + color: #00FFD7 +} + +div.highlight .-Color[class*=-BGC50] { + background-color: #00FFD7 +} + +div.highlight .-Color[class*=-C51] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC51] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C52] { + color: #5F0000 +} + +div.highlight .-Color[class*=-BGC52] { + background-color: #5F0000 +} + +div.highlight .-Color[class*=-C53] { + color: #5F005F +} + +div.highlight .-Color[class*=-BGC53] { + background-color: #5F005F +} + +div.highlight .-Color[class*=-C54] { + color: #5F0087 +} + +div.highlight .-Color[class*=-BGC54] { + background-color: #5F0087 +} + +div.highlight .-Color[class*=-C55] { + color: #5F00AF +} + +div.highlight .-Color[class*=-BGC55] { + background-color: #5F00AF +} + +div.highlight .-Color[class*=-C56] { + color: #5F00D7 +} + +div.highlight .-Color[class*=-BGC56] { + background-color: #5F00D7 +} + +div.highlight .-Color[class*=-C57] { + color: #5F00FF +} + +div.highlight .-Color[class*=-BGC57] { + background-color: #5F00FF +} + +div.highlight .-Color[class*=-C58] { + color: #5F5F00 +} + +div.highlight .-Color[class*=-BGC58] { + background-color: #5F5F00 +} + +div.highlight .-Color[class*=-C59] { + color: #5F5F5F +} + +div.highlight .-Color[class*=-BGC59] { + background-color: #5F5F5F +} + +div.highlight .-Color[class*=-C60] { + color: #5F5F87 +} + +div.highlight .-Color[class*=-BGC60] { + background-color: #5F5F87 +} + +div.highlight .-Color[class*=-C61] { + color: #5F5FAF +} + +div.highlight .-Color[class*=-BGC61] { + background-color: #5F5FAF +} + +div.highlight .-Color[class*=-C62] { + color: #5F5FD7 +} + +div.highlight .-Color[class*=-BGC62] { + background-color: #5F5FD7 +} + +div.highlight .-Color[class*=-C63] { + color: #5F5FFF +} + +div.highlight .-Color[class*=-BGC63] { + background-color: #5F5FFF +} + +div.highlight .-Color[class*=-C64] { + color: #5F8700 +} + +div.highlight .-Color[class*=-BGC64] { + background-color: #5F8700 +} + +div.highlight .-Color[class*=-C65] { + color: #5F875F +} + +div.highlight .-Color[class*=-BGC65] { + background-color: #5F875F +} + +div.highlight .-Color[class*=-C66] { + color: #5F8787 +} + +div.highlight .-Color[class*=-BGC66] { + background-color: #5F8787 +} + +div.highlight .-Color[class*=-C67] { + color: #5F87AF +} + +div.highlight .-Color[class*=-BGC67] { + background-color: #5F87AF +} + +div.highlight .-Color[class*=-C68] { + color: #5F87D7 +} + +div.highlight .-Color[class*=-BGC68] { + background-color: #5F87D7 +} + +div.highlight .-Color[class*=-C69] { + color: #5F87FF +} + +div.highlight .-Color[class*=-BGC69] { + background-color: #5F87FF +} + +div.highlight .-Color[class*=-C70] { + color: #5FAF00 +} + +div.highlight .-Color[class*=-BGC70] { + background-color: #5FAF00 +} + +div.highlight .-Color[class*=-C71] { + color: #5FAF5F +} + +div.highlight .-Color[class*=-BGC71] { + background-color: #5FAF5F +} + +div.highlight .-Color[class*=-C72] { + color: #5FAF87 +} + +div.highlight .-Color[class*=-BGC72] { + background-color: #5FAF87 +} + +div.highlight .-Color[class*=-C73] { + color: #5FAFAF +} + +div.highlight .-Color[class*=-BGC73] { + background-color: #5FAFAF +} + +div.highlight .-Color[class*=-C74] { + color: #5FAFD7 +} + +div.highlight .-Color[class*=-BGC74] { + background-color: #5FAFD7 +} + +div.highlight .-Color[class*=-C75] { + color: #5FAFFF +} + +div.highlight .-Color[class*=-BGC75] { + background-color: #5FAFFF +} + +div.highlight .-Color[class*=-C76] { + color: #5FD700 +} + +div.highlight .-Color[class*=-BGC76] { + background-color: #5FD700 +} + +div.highlight .-Color[class*=-C77] { + color: #5FD75F +} + +div.highlight .-Color[class*=-BGC77] { + background-color: #5FD75F +} + +div.highlight .-Color[class*=-C78] { + color: #5FD787 +} + +div.highlight .-Color[class*=-BGC78] { + background-color: #5FD787 +} + +div.highlight .-Color[class*=-C79] { + color: #5FD7AF +} + +div.highlight .-Color[class*=-BGC79] { + background-color: #5FD7AF +} + +div.highlight .-Color[class*=-C80] { + color: #5FD7D7 +} + +div.highlight .-Color[class*=-BGC80] { + background-color: #5FD7D7 +} + +div.highlight .-Color[class*=-C81] { + color: #5FD7FF +} + +div.highlight .-Color[class*=-BGC81] { + background-color: #5FD7FF +} + +div.highlight .-Color[class*=-C82] { + color: #5FFF00 +} + +div.highlight .-Color[class*=-BGC82] { + background-color: #5FFF00 +} + +div.highlight .-Color[class*=-C83] { + color: #5FFF5F +} + +div.highlight .-Color[class*=-BGC83] { + background-color: #5FFF5F +} + +div.highlight .-Color[class*=-C84] { + color: #5FFF87 +} + +div.highlight .-Color[class*=-BGC84] { + background-color: #5FFF87 +} + +div.highlight .-Color[class*=-C85] { + color: #5FFFAF +} + +div.highlight .-Color[class*=-BGC85] { + background-color: #5FFFAF +} + +div.highlight .-Color[class*=-C86] { + color: #5FFFD7 +} + +div.highlight .-Color[class*=-BGC86] { + background-color: #5FFFD7 +} + +div.highlight .-Color[class*=-C87] { + color: #5FFFFF +} + +div.highlight .-Color[class*=-BGC87] { + background-color: #5FFFFF +} + +div.highlight .-Color[class*=-C88] { + color: #870000 +} + +div.highlight .-Color[class*=-BGC88] { + background-color: #870000 +} + +div.highlight .-Color[class*=-C89] { + color: #87005F +} + +div.highlight .-Color[class*=-BGC89] { + background-color: #87005F +} + +div.highlight .-Color[class*=-C90] { + color: #870087 +} + +div.highlight .-Color[class*=-BGC90] { + background-color: #870087 +} + +div.highlight .-Color[class*=-C91] { + color: #8700AF +} + +div.highlight .-Color[class*=-BGC91] { + background-color: #8700AF +} + +div.highlight .-Color[class*=-C92] { + color: #8700D7 +} + +div.highlight .-Color[class*=-BGC92] { + background-color: #8700D7 +} + +div.highlight .-Color[class*=-C93] { + color: #8700FF +} + +div.highlight .-Color[class*=-BGC93] { + background-color: #8700FF +} + +div.highlight .-Color[class*=-C94] { + color: #875F00 +} + +div.highlight .-Color[class*=-BGC94] { + background-color: #875F00 +} + +div.highlight .-Color[class*=-C95] { + color: #875F5F +} + +div.highlight .-Color[class*=-BGC95] { + background-color: #875F5F +} + +div.highlight .-Color[class*=-C96] { + color: #875F87 +} + +div.highlight .-Color[class*=-BGC96] { + background-color: #875F87 +} + +div.highlight .-Color[class*=-C97] { + color: #875FAF +} + +div.highlight .-Color[class*=-BGC97] { + background-color: #875FAF +} + +div.highlight .-Color[class*=-C98] { + color: #875FD7 +} + +div.highlight .-Color[class*=-BGC98] { + background-color: #875FD7 +} + +div.highlight .-Color[class*=-C99] { + color: #875FFF +} + +div.highlight .-Color[class*=-BGC99] { + background-color: #875FFF +} + +div.highlight .-Color[class*=-C100] { + color: #878700 +} + +div.highlight .-Color[class*=-BGC100] { + background-color: #878700 +} + +div.highlight .-Color[class*=-C101] { + color: #87875F +} + +div.highlight .-Color[class*=-BGC101] { + background-color: #87875F +} + +div.highlight .-Color[class*=-C102] { + color: #878787 +} + +div.highlight .-Color[class*=-BGC102] { + background-color: #878787 +} + +div.highlight .-Color[class*=-C103] { + color: #8787AF +} + +div.highlight .-Color[class*=-BGC103] { + background-color: #8787AF +} + +div.highlight .-Color[class*=-C104] { + color: #8787D7 +} + +div.highlight .-Color[class*=-BGC104] { + background-color: #8787D7 +} + +div.highlight .-Color[class*=-C105] { + color: #8787FF +} + +div.highlight .-Color[class*=-BGC105] { + background-color: #8787FF +} + +div.highlight .-Color[class*=-C106] { + color: #87AF00 +} + +div.highlight .-Color[class*=-BGC106] { + background-color: #87AF00 +} + +div.highlight .-Color[class*=-C107] { + color: #87AF5F +} + +div.highlight .-Color[class*=-BGC107] { + background-color: #87AF5F +} + +div.highlight .-Color[class*=-C108] { + color: #87AF87 +} + +div.highlight .-Color[class*=-BGC108] { + background-color: #87AF87 +} + +div.highlight .-Color[class*=-C109] { + color: #87AFAF +} + +div.highlight .-Color[class*=-BGC109] { + background-color: #87AFAF +} + +div.highlight .-Color[class*=-C110] { + color: #87AFD7 +} + +div.highlight .-Color[class*=-BGC110] { + background-color: #87AFD7 +} + +div.highlight .-Color[class*=-C111] { + color: #87AFFF +} + +div.highlight .-Color[class*=-BGC111] { + background-color: #87AFFF +} + +div.highlight .-Color[class*=-C112] { + color: #87D700 +} + +div.highlight .-Color[class*=-BGC112] { + background-color: #87D700 +} + +div.highlight .-Color[class*=-C113] { + color: #87D75F +} + +div.highlight .-Color[class*=-BGC113] { + background-color: #87D75F +} + +div.highlight .-Color[class*=-C114] { + color: #87D787 +} + +div.highlight .-Color[class*=-BGC114] { + background-color: #87D787 +} + +div.highlight .-Color[class*=-C115] { + color: #87D7AF +} + +div.highlight .-Color[class*=-BGC115] { + background-color: #87D7AF +} + +div.highlight .-Color[class*=-C116] { + color: #87D7D7 +} + +div.highlight .-Color[class*=-BGC116] { + background-color: #87D7D7 +} + +div.highlight .-Color[class*=-C117] { + color: #87D7FF +} + +div.highlight .-Color[class*=-BGC117] { + background-color: #87D7FF +} + +div.highlight .-Color[class*=-C118] { + color: #87FF00 +} + +div.highlight .-Color[class*=-BGC118] { + background-color: #87FF00 +} + +div.highlight .-Color[class*=-C119] { + color: #87FF5F +} + +div.highlight .-Color[class*=-BGC119] { + background-color: #87FF5F +} + +div.highlight .-Color[class*=-C120] { + color: #87FF87 +} + +div.highlight .-Color[class*=-BGC120] { + background-color: #87FF87 +} + +div.highlight .-Color[class*=-C121] { + color: #87FFAF +} + +div.highlight .-Color[class*=-BGC121] { + background-color: #87FFAF +} + +div.highlight .-Color[class*=-C122] { + color: #87FFD7 +} + +div.highlight .-Color[class*=-BGC122] { + background-color: #87FFD7 +} + +div.highlight .-Color[class*=-C123] { + color: #87FFFF +} + +div.highlight .-Color[class*=-BGC123] { + background-color: #87FFFF +} + +div.highlight .-Color[class*=-C124] { + color: #AF0000 +} + +div.highlight .-Color[class*=-BGC124] { + background-color: #AF0000 +} + +div.highlight .-Color[class*=-C125] { + color: #AF005F +} + +div.highlight .-Color[class*=-BGC125] { + background-color: #AF005F +} + +div.highlight .-Color[class*=-C126] { + color: #AF0087 +} + +div.highlight .-Color[class*=-BGC126] { + background-color: #AF0087 +} + +div.highlight .-Color[class*=-C127] { + color: #AF00AF +} + +div.highlight .-Color[class*=-BGC127] { + background-color: #AF00AF +} + +div.highlight .-Color[class*=-C128] { + color: #AF00D7 +} + +div.highlight .-Color[class*=-BGC128] { + background-color: #AF00D7 +} + +div.highlight .-Color[class*=-C129] { + color: #AF00FF +} + +div.highlight .-Color[class*=-BGC129] { + background-color: #AF00FF +} + +div.highlight .-Color[class*=-C130] { + color: #AF5F00 +} + +div.highlight .-Color[class*=-BGC130] { + background-color: #AF5F00 +} + +div.highlight .-Color[class*=-C131] { + color: #AF5F5F +} + +div.highlight .-Color[class*=-BGC131] { + background-color: #AF5F5F +} + +div.highlight .-Color[class*=-C132] { + color: #AF5F87 +} + +div.highlight .-Color[class*=-BGC132] { + background-color: #AF5F87 +} + +div.highlight .-Color[class*=-C133] { + color: #AF5FAF +} + +div.highlight .-Color[class*=-BGC133] { + background-color: #AF5FAF +} + +div.highlight .-Color[class*=-C134] { + color: #AF5FD7 +} + +div.highlight .-Color[class*=-BGC134] { + background-color: #AF5FD7 +} + +div.highlight .-Color[class*=-C135] { + color: #AF5FFF +} + +div.highlight .-Color[class*=-BGC135] { + background-color: #AF5FFF +} + +div.highlight .-Color[class*=-C136] { + color: #AF8700 +} + +div.highlight .-Color[class*=-BGC136] { + background-color: #AF8700 +} + +div.highlight .-Color[class*=-C137] { + color: #AF875F +} + +div.highlight .-Color[class*=-BGC137] { + background-color: #AF875F +} + +div.highlight .-Color[class*=-C138] { + color: #AF8787 +} + +div.highlight .-Color[class*=-BGC138] { + background-color: #AF8787 +} + +div.highlight .-Color[class*=-C139] { + color: #AF87AF +} + +div.highlight .-Color[class*=-BGC139] { + background-color: #AF87AF +} + +div.highlight .-Color[class*=-C140] { + color: #AF87D7 +} + +div.highlight .-Color[class*=-BGC140] { + background-color: #AF87D7 +} + +div.highlight .-Color[class*=-C141] { + color: #AF87FF +} + +div.highlight .-Color[class*=-BGC141] { + background-color: #AF87FF +} + +div.highlight .-Color[class*=-C142] { + color: #AFAF00 +} + +div.highlight .-Color[class*=-BGC142] { + background-color: #AFAF00 +} + +div.highlight .-Color[class*=-C143] { + color: #AFAF5F +} + +div.highlight .-Color[class*=-BGC143] { + background-color: #AFAF5F +} + +div.highlight .-Color[class*=-C144] { + color: #AFAF87 +} + +div.highlight .-Color[class*=-BGC144] { + background-color: #AFAF87 +} + +div.highlight .-Color[class*=-C145] { + color: #AFAFAF +} + +div.highlight .-Color[class*=-BGC145] { + background-color: #AFAFAF +} + +div.highlight .-Color[class*=-C146] { + color: #AFAFD7 +} + +div.highlight .-Color[class*=-BGC146] { + background-color: #AFAFD7 +} + +div.highlight .-Color[class*=-C147] { + color: #AFAFFF +} + +div.highlight .-Color[class*=-BGC147] { + background-color: #AFAFFF +} + +div.highlight .-Color[class*=-C148] { + color: #AFD700 +} + +div.highlight .-Color[class*=-BGC148] { + background-color: #AFD700 +} + +div.highlight .-Color[class*=-C149] { + color: #AFD75F +} + +div.highlight .-Color[class*=-BGC149] { + background-color: #AFD75F +} + +div.highlight .-Color[class*=-C150] { + color: #AFD787 +} + +div.highlight .-Color[class*=-BGC150] { + background-color: #AFD787 +} + +div.highlight .-Color[class*=-C151] { + color: #AFD7AF +} + +div.highlight .-Color[class*=-BGC151] { + background-color: #AFD7AF +} + +div.highlight .-Color[class*=-C152] { + color: #AFD7D7 +} + +div.highlight .-Color[class*=-BGC152] { + background-color: #AFD7D7 +} + +div.highlight .-Color[class*=-C153] { + color: #AFD7FF +} + +div.highlight .-Color[class*=-BGC153] { + background-color: #AFD7FF +} + +div.highlight .-Color[class*=-C154] { + color: #AFFF00 +} + +div.highlight .-Color[class*=-BGC154] { + background-color: #AFFF00 +} + +div.highlight .-Color[class*=-C155] { + color: #AFFF5F +} + +div.highlight .-Color[class*=-BGC155] { + background-color: #AFFF5F +} + +div.highlight .-Color[class*=-C156] { + color: #AFFF87 +} + +div.highlight .-Color[class*=-BGC156] { + background-color: #AFFF87 +} + +div.highlight .-Color[class*=-C157] { + color: #AFFFAF +} + +div.highlight .-Color[class*=-BGC157] { + background-color: #AFFFAF +} + +div.highlight .-Color[class*=-C158] { + color: #AFFFD7 +} + +div.highlight .-Color[class*=-BGC158] { + background-color: #AFFFD7 +} + +div.highlight .-Color[class*=-C159] { + color: #AFFFFF +} + +div.highlight .-Color[class*=-BGC159] { + background-color: #AFFFFF +} + +div.highlight .-Color[class*=-C160] { + color: #D70000 +} + +div.highlight .-Color[class*=-BGC160] { + background-color: #D70000 +} + +div.highlight .-Color[class*=-C161] { + color: #D7005F +} + +div.highlight .-Color[class*=-BGC161] { + background-color: #D7005F +} + +div.highlight .-Color[class*=-C162] { + color: #D70087 +} + +div.highlight .-Color[class*=-BGC162] { + background-color: #D70087 +} + +div.highlight .-Color[class*=-C163] { + color: #D700AF +} + +div.highlight .-Color[class*=-BGC163] { + background-color: #D700AF +} + +div.highlight .-Color[class*=-C164] { + color: #D700D7 +} + +div.highlight .-Color[class*=-BGC164] { + background-color: #D700D7 +} + +div.highlight .-Color[class*=-C165] { + color: #D700FF +} + +div.highlight .-Color[class*=-BGC165] { + background-color: #D700FF +} + +div.highlight .-Color[class*=-C166] { + color: #D75F00 +} + +div.highlight .-Color[class*=-BGC166] { + background-color: #D75F00 +} + +div.highlight .-Color[class*=-C167] { + color: #D75F5F +} + +div.highlight .-Color[class*=-BGC167] { + background-color: #D75F5F +} + +div.highlight .-Color[class*=-C168] { + color: #D75F87 +} + +div.highlight .-Color[class*=-BGC168] { + background-color: #D75F87 +} + +div.highlight .-Color[class*=-C169] { + color: #D75FAF +} + +div.highlight .-Color[class*=-BGC169] { + background-color: #D75FAF +} + +div.highlight .-Color[class*=-C170] { + color: #D75FD7 +} + +div.highlight .-Color[class*=-BGC170] { + background-color: #D75FD7 +} + +div.highlight .-Color[class*=-C171] { + color: #D75FFF +} + +div.highlight .-Color[class*=-BGC171] { + background-color: #D75FFF +} + +div.highlight .-Color[class*=-C172] { + color: #D78700 +} + +div.highlight .-Color[class*=-BGC172] { + background-color: #D78700 +} + +div.highlight .-Color[class*=-C173] { + color: #D7875F +} + +div.highlight .-Color[class*=-BGC173] { + background-color: #D7875F +} + +div.highlight .-Color[class*=-C174] { + color: #D78787 +} + +div.highlight .-Color[class*=-BGC174] { + background-color: #D78787 +} + +div.highlight .-Color[class*=-C175] { + color: #D787AF +} + +div.highlight .-Color[class*=-BGC175] { + background-color: #D787AF +} + +div.highlight .-Color[class*=-C176] { + color: #D787D7 +} + +div.highlight .-Color[class*=-BGC176] { + background-color: #D787D7 +} + +div.highlight .-Color[class*=-C177] { + color: #D787FF +} + +div.highlight .-Color[class*=-BGC177] { + background-color: #D787FF +} + +div.highlight .-Color[class*=-C178] { + color: #D7AF00 +} + +div.highlight .-Color[class*=-BGC178] { + background-color: #D7AF00 +} + +div.highlight .-Color[class*=-C179] { + color: #D7AF5F +} + +div.highlight .-Color[class*=-BGC179] { + background-color: #D7AF5F +} + +div.highlight .-Color[class*=-C180] { + color: #D7AF87 +} + +div.highlight .-Color[class*=-BGC180] { + background-color: #D7AF87 +} + +div.highlight .-Color[class*=-C181] { + color: #D7AFAF +} + +div.highlight .-Color[class*=-BGC181] { + background-color: #D7AFAF +} + +div.highlight .-Color[class*=-C182] { + color: #D7AFD7 +} + +div.highlight .-Color[class*=-BGC182] { + background-color: #D7AFD7 +} + +div.highlight .-Color[class*=-C183] { + color: #D7AFFF +} + +div.highlight .-Color[class*=-BGC183] { + background-color: #D7AFFF +} + +div.highlight .-Color[class*=-C184] { + color: #D7D700 +} + +div.highlight .-Color[class*=-BGC184] { + background-color: #D7D700 +} + +div.highlight .-Color[class*=-C185] { + color: #D7D75F +} + +div.highlight .-Color[class*=-BGC185] { + background-color: #D7D75F +} + +div.highlight .-Color[class*=-C186] { + color: #D7D787 +} + +div.highlight .-Color[class*=-BGC186] { + background-color: #D7D787 +} + +div.highlight .-Color[class*=-C187] { + color: #D7D7AF +} + +div.highlight .-Color[class*=-BGC187] { + background-color: #D7D7AF +} + +div.highlight .-Color[class*=-C188] { + color: #D7D7D7 +} + +div.highlight .-Color[class*=-BGC188] { + background-color: #D7D7D7 +} + +div.highlight .-Color[class*=-C189] { + color: #D7D7FF +} + +div.highlight .-Color[class*=-BGC189] { + background-color: #D7D7FF +} + +div.highlight .-Color[class*=-C190] { + color: #D7FF00 +} + +div.highlight .-Color[class*=-BGC190] { + background-color: #D7FF00 +} + +div.highlight .-Color[class*=-C191] { + color: #D7FF5F +} + +div.highlight .-Color[class*=-BGC191] { + background-color: #D7FF5F +} + +div.highlight .-Color[class*=-C192] { + color: #D7FF87 +} + +div.highlight .-Color[class*=-BGC192] { + background-color: #D7FF87 +} + +div.highlight .-Color[class*=-C193] { + color: #D7FFAF +} + +div.highlight .-Color[class*=-BGC193] { + background-color: #D7FFAF +} + +div.highlight .-Color[class*=-C194] { + color: #D7FFD7 +} + +div.highlight .-Color[class*=-BGC194] { + background-color: #D7FFD7 +} + +div.highlight .-Color[class*=-C195] { + color: #D7FFFF +} + +div.highlight .-Color[class*=-BGC195] { + background-color: #D7FFFF +} + +div.highlight .-Color[class*=-C196] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC196] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C197] { + color: #FF005F +} + +div.highlight .-Color[class*=-BGC197] { + background-color: #FF005F +} + +div.highlight .-Color[class*=-C198] { + color: #FF0087 +} + +div.highlight .-Color[class*=-BGC198] { + background-color: #FF0087 +} + +div.highlight .-Color[class*=-C199] { + color: #FF00AF +} + +div.highlight .-Color[class*=-BGC199] { + background-color: #FF00AF +} + +div.highlight .-Color[class*=-C200] { + color: #FF00D7 +} + +div.highlight .-Color[class*=-BGC200] { + background-color: #FF00D7 +} + +div.highlight .-Color[class*=-C201] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC201] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C202] { + color: #FF5F00 +} + +div.highlight .-Color[class*=-BGC202] { + background-color: #FF5F00 +} + +div.highlight .-Color[class*=-C203] { + color: #FF5F5F +} + +div.highlight .-Color[class*=-BGC203] { + background-color: #FF5F5F +} + +div.highlight .-Color[class*=-C204] { + color: #FF5F87 +} + +div.highlight .-Color[class*=-BGC204] { + background-color: #FF5F87 +} + +div.highlight .-Color[class*=-C205] { + color: #FF5FAF +} + +div.highlight .-Color[class*=-BGC205] { + background-color: #FF5FAF +} + +div.highlight .-Color[class*=-C206] { + color: #FF5FD7 +} + +div.highlight .-Color[class*=-BGC206] { + background-color: #FF5FD7 +} + +div.highlight .-Color[class*=-C207] { + color: #FF5FFF +} + +div.highlight .-Color[class*=-BGC207] { + background-color: #FF5FFF +} + +div.highlight .-Color[class*=-C208] { + color: #FF8700 +} + +div.highlight .-Color[class*=-BGC208] { + background-color: #FF8700 +} + +div.highlight .-Color[class*=-C209] { + color: #FF875F +} + +div.highlight .-Color[class*=-BGC209] { + background-color: #FF875F +} + +div.highlight .-Color[class*=-C210] { + color: #FF8787 +} + +div.highlight .-Color[class*=-BGC210] { + background-color: #FF8787 +} + +div.highlight .-Color[class*=-C211] { + color: #FF87AF +} + +div.highlight .-Color[class*=-BGC211] { + background-color: #FF87AF +} + +div.highlight .-Color[class*=-C212] { + color: #FF87D7 +} + +div.highlight .-Color[class*=-BGC212] { + background-color: #FF87D7 +} + +div.highlight .-Color[class*=-C213] { + color: #FF87FF +} + +div.highlight .-Color[class*=-BGC213] { + background-color: #FF87FF +} + +div.highlight .-Color[class*=-C214] { + color: #FFAF00 +} + +div.highlight .-Color[class*=-BGC214] { + background-color: #FFAF00 +} + +div.highlight .-Color[class*=-C215] { + color: #FFAF5F +} + +div.highlight .-Color[class*=-BGC215] { + background-color: #FFAF5F +} + +div.highlight .-Color[class*=-C216] { + color: #FFAF87 +} + +div.highlight .-Color[class*=-BGC216] { + background-color: #FFAF87 +} + +div.highlight .-Color[class*=-C217] { + color: #FFAFAF +} + +div.highlight .-Color[class*=-BGC217] { + background-color: #FFAFAF +} + +div.highlight .-Color[class*=-C218] { + color: #FFAFD7 +} + +div.highlight .-Color[class*=-BGC218] { + background-color: #FFAFD7 +} + +div.highlight .-Color[class*=-C219] { + color: #FFAFFF +} + +div.highlight .-Color[class*=-BGC219] { + background-color: #FFAFFF +} + +div.highlight .-Color[class*=-C220] { + color: #FFD700 +} + +div.highlight .-Color[class*=-BGC220] { + background-color: #FFD700 +} + +div.highlight .-Color[class*=-C221] { + color: #FFD75F +} + +div.highlight .-Color[class*=-BGC221] { + background-color: #FFD75F +} + +div.highlight .-Color[class*=-C222] { + color: #FFD787 +} + +div.highlight .-Color[class*=-BGC222] { + background-color: #FFD787 +} + +div.highlight .-Color[class*=-C223] { + color: #FFD7AF +} + +div.highlight .-Color[class*=-BGC223] { + background-color: #FFD7AF +} + +div.highlight .-Color[class*=-C224] { + color: #FFD7D7 +} + +div.highlight .-Color[class*=-BGC224] { + background-color: #FFD7D7 +} + +div.highlight .-Color[class*=-C225] { + color: #FFD7FF +} + +div.highlight .-Color[class*=-BGC225] { + background-color: #FFD7FF +} + +div.highlight .-Color[class*=-C226] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC226] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C227] { + color: #FFFF5F +} + +div.highlight .-Color[class*=-BGC227] { + background-color: #FFFF5F +} + +div.highlight .-Color[class*=-C228] { + color: #FFFF87 +} + +div.highlight .-Color[class*=-BGC228] { + background-color: #FFFF87 +} + +div.highlight .-Color[class*=-C229] { + color: #FFFFAF +} + +div.highlight .-Color[class*=-BGC229] { + background-color: #FFFFAF +} + +div.highlight .-Color[class*=-C230] { + color: #FFFFD7 +} + +div.highlight .-Color[class*=-BGC230] { + background-color: #FFFFD7 +} + +div.highlight .-Color[class*=-C231] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC231] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C232] { + color: #080808 +} + +div.highlight .-Color[class*=-BGC232] { + background-color: #080808 +} + +div.highlight .-Color[class*=-C233] { + color: #121212 +} + +div.highlight .-Color[class*=-BGC233] { + background-color: #121212 +} + +div.highlight .-Color[class*=-C234] { + color: #1C1C1C +} + +div.highlight .-Color[class*=-BGC234] { + background-color: #1C1C1C +} + +div.highlight .-Color[class*=-C235] { + color: #262626 +} + +div.highlight .-Color[class*=-BGC235] { + background-color: #262626 +} + +div.highlight .-Color[class*=-C236] { + color: #303030 +} + +div.highlight .-Color[class*=-BGC236] { + background-color: #303030 +} + +div.highlight .-Color[class*=-C237] { + color: #3A3A3A +} + +div.highlight .-Color[class*=-BGC237] { + background-color: #3A3A3A +} + +div.highlight .-Color[class*=-C238] { + color: #444444 +} + +div.highlight .-Color[class*=-BGC238] { + background-color: #444444 +} + +div.highlight .-Color[class*=-C239] { + color: #4E4E4E +} + +div.highlight .-Color[class*=-BGC239] { + background-color: #4E4E4E +} + +div.highlight .-Color[class*=-C240] { + color: #585858 +} + +div.highlight .-Color[class*=-BGC240] { + background-color: #585858 +} + +div.highlight .-Color[class*=-C241] { + color: #626262 +} + +div.highlight .-Color[class*=-BGC241] { + background-color: #626262 +} + +div.highlight .-Color[class*=-C242] { + color: #6C6C6C +} + +div.highlight .-Color[class*=-BGC242] { + background-color: #6C6C6C +} + +div.highlight .-Color[class*=-C243] { + color: #767676 +} + +div.highlight .-Color[class*=-BGC243] { + background-color: #767676 +} + +div.highlight .-Color[class*=-C244] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC244] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C245] { + color: #8A8A8A +} + +div.highlight .-Color[class*=-BGC245] { + background-color: #8A8A8A +} + +div.highlight .-Color[class*=-C246] { + color: #949494 +} + +div.highlight .-Color[class*=-BGC246] { + background-color: #949494 +} + +div.highlight .-Color[class*=-C247] { + color: #9E9E9E +} + +div.highlight .-Color[class*=-BGC247] { + background-color: #9E9E9E +} + +div.highlight .-Color[class*=-C248] { + color: #A8A8A8 +} + +div.highlight .-Color[class*=-BGC248] { + background-color: #A8A8A8 +} + +div.highlight .-Color[class*=-C249] { + color: #B2B2B2 +} + +div.highlight .-Color[class*=-BGC249] { + background-color: #B2B2B2 +} + +div.highlight .-Color[class*=-C250] { + color: #BCBCBC +} + +div.highlight .-Color[class*=-BGC250] { + background-color: #BCBCBC +} + +div.highlight .-Color[class*=-C251] { + color: #C6C6C6 +} + +div.highlight .-Color[class*=-BGC251] { + background-color: #C6C6C6 +} + +div.highlight .-Color[class*=-C252] { + color: #D0D0D0 +} + +div.highlight .-Color[class*=-BGC252] { + background-color: #D0D0D0 +} + +div.highlight .-Color[class*=-C253] { + color: #DADADA +} + +div.highlight .-Color[class*=-BGC253] { + background-color: #DADADA +} + +div.highlight .-Color[class*=-C254] { + color: #E4E4E4 +} + +div.highlight .-Color[class*=-BGC254] { + background-color: #E4E4E4 +} + +div.highlight .-Color[class*=-C255] { + color: #EEEEEE +} + +div.highlight .-Color[class*=-BGC255] { + background-color: #EEEEEE +} diff --git a/singlehtml/_static/plus.png b/singlehtml/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/singlehtml/_static/plus.png differ diff --git a/singlehtml/_static/pygments.css b/singlehtml/_static/pygments.css new file mode 100644 index 0000000..84ab303 --- /dev/null +++ b/singlehtml/_static/pygments.css @@ -0,0 +1,75 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ +.highlight .err { border: 1px solid #FF0000 } /* Error */ +.highlight .k { color: #008000; font-weight: bold } /* Keyword */ +.highlight .o { color: #666666 } /* Operator */ +.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #9C6500 } /* Comment.Preproc */ +.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #E40000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #008400 } /* Generic.Inserted */ +.highlight .go { color: #717171 } /* Generic.Output */ +.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #008000 } /* Keyword.Pseudo */ +.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #B00040 } /* Keyword.Type */ +.highlight .m { color: #666666 } /* Literal.Number */ +.highlight .s { color: #BA2121 } /* Literal.String */ +.highlight .na { color: #687822 } /* Name.Attribute */ +.highlight .nb { color: #008000 } /* Name.Builtin */ +.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.highlight .no { color: #880000 } /* Name.Constant */ +.highlight .nd { color: #AA22FF } /* Name.Decorator */ +.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0000FF } /* Name.Function */ +.highlight .nl { color: #767600 } /* Name.Label */ +.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #19177C } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #666666 } /* Literal.Number.Bin */ +.highlight .mf { color: #666666 } /* Literal.Number.Float */ +.highlight .mh { color: #666666 } /* Literal.Number.Hex */ +.highlight .mi { color: #666666 } /* Literal.Number.Integer */ +.highlight .mo { color: #666666 } /* Literal.Number.Oct */ +.highlight .sa { color: #BA2121 } /* Literal.String.Affix */ +.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ +.highlight .sc { color: #BA2121 } /* Literal.String.Char */ +.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ +.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #BA2121 } /* Literal.String.Double */ +.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ +.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ +.highlight .sx { color: #008000 } /* Literal.String.Other */ +.highlight .sr { color: #A45A77 } /* Literal.String.Regex */ +.highlight .s1 { color: #BA2121 } /* Literal.String.Single */ +.highlight .ss { color: #19177C } /* Literal.String.Symbol */ +.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0000FF } /* Name.Function.Magic */ +.highlight .vc { color: #19177C } /* Name.Variable.Class */ +.highlight .vg { color: #19177C } /* Name.Variable.Global */ +.highlight .vi { color: #19177C } /* Name.Variable.Instance */ +.highlight .vm { color: #19177C } /* Name.Variable.Magic */ +.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/singlehtml/_static/searchtools.js b/singlehtml/_static/searchtools.js new file mode 100644 index 0000000..b08d58c --- /dev/null +++ b/singlehtml/_static/searchtools.js @@ -0,0 +1,620 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + "Search finished, found ${resultCount} page(s) matching the search query." + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + const score = Math.round(Scorer.title * queryLower.length / title.length); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/singlehtml/_static/sphinx_highlight.js b/singlehtml/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/singlehtml/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/singlehtml/_static/sphinx_lesson.css b/singlehtml/_static/sphinx_lesson.css new file mode 100644 index 0000000..14b20c6 --- /dev/null +++ b/singlehtml/_static/sphinx_lesson.css @@ -0,0 +1,51 @@ +/* sphinx_lesson.css */ + +body.wy-body-for-nav img.with-border { + border: 2px solid; +} + +.rst-content .admonition-no-content { + padding-bottom: 0px; +} + +.rst-content .demo > .admonition-title::before { + content: "\01F440"; /* Eyes */ } +.rst-content .type-along > .admonition-title::before { + content: "\02328\0FE0F"; /* Keyboard */ } +.rst-content .exercise > .admonition-title::before { + content: "\0270D\0FE0F"; /* Hand */ } +.rst-content .solution > .admonition-title::before { + content: "\02714\0FE0E"; /* Check mark */ } +.rst-content .homework > .admonition-title::before { + content: "\01F4DD"; /* Memo */ } +.rst-content .discussion > .admonition-title::before { + content: "\01F4AC"; /* Speech balloon */ } +.rst-content .questions > .admonition-title::before { + content: "\02753\0FE0E"; /* Question mark */ } +.rst-content .prerequisites > .admonition-title::before { + content: "\02699"; /* Gear */ } +.rst-content .seealso > .admonition-title::before { + content: "\027A1\0FE0E"; /* Question mark */ } + + +/* instructor-note */ +.rst-content .instructor-note { + background: #e7e7e7; +} +.rst-content .instructor-note > .admonition-title { + background: #6a6a6a; +} +.rst-content .instructor-note > .admonition-title::before { + content: ""; +} + + +/* sphinx_toggle_button, make the font white */ +.rst-content .toggle.admonition button.toggle-button { + color: white; +} + +/* sphinx-togglebutton, remove underflow when toggled to hidden mode */ +.rst-content .admonition.toggle-hidden { + padding-bottom: 0px; +} diff --git a/singlehtml/_static/sphinx_rtd_theme_ext_color_contrast.css b/singlehtml/_static/sphinx_rtd_theme_ext_color_contrast.css new file mode 100644 index 0000000..e68feb8 --- /dev/null +++ b/singlehtml/_static/sphinx_rtd_theme_ext_color_contrast.css @@ -0,0 +1,47 @@ +/* The following are for web accessibility of sphinx_rtd_theme: they + * solve some of the most frequent contrast issues. Remove when this + * solved: + * https://github.com/readthedocs/sphinx_rtd_theme/issues/971 + */ +/* background: #fcfcfc, note boxes #E7F2FA */ +a { color: #2573A7; } /* original #2980B9, #1F5C84; */ +body { color: #242424; } /* original #404040, #383838 */ +.wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a { + color: #ffffff; +} /* original #fcfcfc */ +footer { color: #737373; } /* original gray=#808080*/ +footer span.commit code, footer span.commit .rst-content tt, .rst-content footer span.commit tt { + color: #737373; +} /* original gray=#808080*/ +.rst-content tt.literal, .rst-content tt.literal, .rst-content code.literal { + color: #AB2314; +} +/* Sidebar background */ +.wy-side-nav-search { background-color: #277CB4;} + +/* Same, but for pygments */ +.highlight .ch { color: #3E7A89; } /* #! line */ +.highlight .c1 { color: #3E7A89; } /* also comments */ +.highlight .nv { color: #AD3ECC; } /* variable */ +.highlight .gp { color: #B45608; } /* prompt character, $*/ +.highlight .si { color: #3975B1; } /* ${} variable text */ +.highlight .nc { color: #0C78A7; } + +/* Sphinx admonitions */ +/* warning */ +.wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .rst-content .wy-alert-warning.admonition .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title, .rst-content .wy-alert-warning.admonition .admonition-title { + background: #B15E16; } +/* important */ +.wy-alert.wy-alert-success .wy-alert-title, .rst-content .wy-alert-success.note .wy-alert-title, .rst-content .wy-alert-success.attention .wy-alert-title, .rst-content .wy-alert-success.caution .wy-alert-title, .rst-content .wy-alert-success.danger .wy-alert-title, .rst-content .wy-alert-success.error .wy-alert-title, .rst-content .hint .wy-alert-title, .rst-content .important .wy-alert-title, .rst-content .tip .wy-alert-title, .rst-content .wy-alert-success.warning .wy-alert-title, .rst-content .wy-alert-success.seealso .wy-alert-title, .rst-content .wy-alert-success.admonition-todo .wy-alert-title, .rst-content .wy-alert-success.admonition .wy-alert-title, .wy-alert.wy-alert-success .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-success .admonition-title, .rst-content .wy-alert-success.note .admonition-title, .rst-content .wy-alert-success.attention .admonition-title, .rst-content .wy-alert-success.caution .admonition-title, .rst-content .wy-alert-success.danger .admonition-title, .rst-content .wy-alert-success.error .admonition-title, .rst-content .hint .admonition-title, .rst-content .important .admonition-title, .rst-content .tip .admonition-title, .rst-content .wy-alert-success.warning .admonition-title, .rst-content .wy-alert-success.seealso .admonition-title, .rst-content .wy-alert-success.admonition-todo .admonition-title, .rst-content .wy-alert-success.admonition .admonition-title { + background: #12826C; } +/* seealso, note, etc */ +.wy-alert.wy-alert-info .wy-alert-title, .rst-content .note .wy-alert-title, .rst-content .wy-alert-info.attention .wy-alert-title, .rst-content .wy-alert-info.caution .wy-alert-title, .rst-content .wy-alert-info.danger .wy-alert-title, .rst-content .wy-alert-info.error .wy-alert-title, .rst-content .wy-alert-info.hint .wy-alert-title, .rst-content .wy-alert-info.important .wy-alert-title, .rst-content .wy-alert-info.tip .wy-alert-title, .rst-content .wy-alert-info.warning .wy-alert-title, .rst-content .seealso .wy-alert-title, .rst-content .wy-alert-info.admonition-todo .wy-alert-title, .rst-content .wy-alert-info.admonition .wy-alert-title, .wy-alert.wy-alert-info .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-info .admonition-title, .rst-content .note .admonition-title, .rst-content .wy-alert-info.attention .admonition-title, .rst-content .wy-alert-info.caution .admonition-title, .rst-content .wy-alert-info.danger .admonition-title, .rst-content .wy-alert-info.error .admonition-title, .rst-content .wy-alert-info.hint .admonition-title, .rst-content .wy-alert-info.important .admonition-title, .rst-content .wy-alert-info.tip .admonition-title, .rst-content .wy-alert-info.warning .admonition-title, .rst-content .seealso .admonition-title, .rst-content .wy-alert-info.admonition-todo .admonition-title, .rst-content .wy-alert-info.admonition .admonition-title { + background: #277CB4; } +/* error, danger */ +.rst-content .danger .admonition-title, .rst-content .danger .wy-alert-title, .rst-content .error .admonition-title, .rst-content .error .wy-alert-title, .rst-content .wy-alert-danger.admonition-todo .admonition-title, .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, .rst-content .wy-alert-danger.admonition .admonition-title, .rst-content .wy-alert-danger.admonition .wy-alert-title, .rst-content .wy-alert-danger.attention .admonition-title, .rst-content .wy-alert-danger.attention .wy-alert-title, .rst-content .wy-alert-danger.caution .admonition-title, .rst-content .wy-alert-danger.caution .wy-alert-title, .rst-content .wy-alert-danger.hint .admonition-title, .rst-content .wy-alert-danger.hint .wy-alert-title, .rst-content .wy-alert-danger.important .admonition-title, .rst-content .wy-alert-danger.important .wy-alert-title, .rst-content .wy-alert-danger.note .admonition-title, .rst-content .wy-alert-danger.note .wy-alert-title, .rst-content .wy-alert-danger.seealso .admonition-title, .rst-content .wy-alert-danger.seealso .wy-alert-title, .rst-content .wy-alert-danger.tip .admonition-title, .rst-content .wy-alert-danger.tip .wy-alert-title, .rst-content .wy-alert-danger.warning .admonition-title, .rst-content .wy-alert-danger.warning .wy-alert-title, .rst-content .wy-alert.wy-alert-danger .admonition-title, .wy-alert.wy-alert-danger .rst-content .admonition-title, .wy-alert.wy-alert-danger .wy-alert-title { + background: #e31704; +} + +/* Generic admonition titles */ +.wy-alert-title, .rst-content .admonition-title { + background: #277CB4; } diff --git a/singlehtml/_static/style.css b/singlehtml/_static/style.css new file mode 100644 index 0000000..59e60ee --- /dev/null +++ b/singlehtml/_static/style.css @@ -0,0 +1,6 @@ +.rst-content .objectives { + background: #fee0d2; +} +.rst-content .objectives > .admonition-title { + background: #fc9272; +} diff --git a/singlehtml/_static/tabs.css b/singlehtml/_static/tabs.css new file mode 100644 index 0000000..957ba60 --- /dev/null +++ b/singlehtml/_static/tabs.css @@ -0,0 +1,89 @@ +.sphinx-tabs { + margin-bottom: 1rem; +} + +[role="tablist"] { + border-bottom: 1px solid #a0b3bf; +} + +.sphinx-tabs-tab { + position: relative; + font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif; + color: #1D5C87; + line-height: 24px; + margin: 0; + font-size: 16px; + font-weight: 400; + background-color: rgba(255, 255, 255, 0); + border-radius: 5px 5px 0 0; + border: 0; + padding: 1rem 1.5rem; + margin-bottom: 0; +} + +.sphinx-tabs-tab[aria-selected="true"] { + font-weight: 700; + border: 1px solid #a0b3bf; + border-bottom: 1px solid white; + margin: -1px; + background-color: white; +} + +.sphinx-tabs-tab:focus { + z-index: 1; + outline-offset: 1px; +} + +.sphinx-tabs-panel { + position: relative; + padding: 1rem; + border: 1px solid #a0b3bf; + margin: 0px -1px -1px -1px; + border-radius: 0 0 5px 5px; + border-top: 0; + background: white; +} + +.sphinx-tabs-panel.code-tab { + padding: 0.4rem; +} + +.sphinx-tab img { + margin-bottom: 24 px; +} + +/* Dark theme preference styling */ + +@media (prefers-color-scheme: dark) { + body[data-theme="auto"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); + } + + body[data-theme="auto"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); + } + + body[data-theme="auto"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 1px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); + } +} + +/* Explicit dark theme styling */ + +body[data-theme="dark"] .sphinx-tabs-panel { + color: white; + background-color: rgb(50, 50, 50); +} + +body[data-theme="dark"] .sphinx-tabs-tab { + color: white; + background-color: rgba(255, 255, 255, 0.05); +} + +body[data-theme="dark"] .sphinx-tabs-tab[aria-selected="true"] { + border-bottom: 2px solid rgb(50, 50, 50); + background-color: rgb(50, 50, 50); +} diff --git a/singlehtml/_static/tabs.js b/singlehtml/_static/tabs.js new file mode 100644 index 0000000..48dc303 --- /dev/null +++ b/singlehtml/_static/tabs.js @@ -0,0 +1,145 @@ +try { + var session = window.sessionStorage || {}; +} catch (e) { + var session = {}; +} + +window.addEventListener("DOMContentLoaded", () => { + const allTabs = document.querySelectorAll('.sphinx-tabs-tab'); + const tabLists = document.querySelectorAll('[role="tablist"]'); + + allTabs.forEach(tab => { + tab.addEventListener("click", changeTabs); + }); + + tabLists.forEach(tabList => { + tabList.addEventListener("keydown", keyTabs); + }); + + // Restore group tab selection from session + const lastSelected = session.getItem('sphinx-tabs-last-selected'); + if (lastSelected != null) selectNamedTabs(lastSelected); +}); + +/** + * Key focus left and right between sibling elements using arrows + * @param {Node} e the element in focus when key was pressed + */ +function keyTabs(e) { + const tab = e.target; + let nextTab = null; + if (e.keyCode === 39 || e.keyCode === 37) { + tab.setAttribute("tabindex", -1); + // Move right + if (e.keyCode === 39) { + nextTab = tab.nextElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.firstElementChild; + } + // Move left + } else if (e.keyCode === 37) { + nextTab = tab.previousElementSibling; + if (nextTab === null) { + nextTab = tab.parentNode.lastElementChild; + } + } + } + + if (nextTab !== null) { + nextTab.setAttribute("tabindex", 0); + nextTab.focus(); + } +} + +/** + * Select or deselect clicked tab. If a group tab + * is selected, also select tab in other tabLists. + * @param {Node} e the element that was clicked + */ +function changeTabs(e) { + // Use this instead of the element that was clicked, in case it's a child + const notSelected = this.getAttribute("aria-selected") === "false"; + const positionBefore = this.parentNode.getBoundingClientRect().top; + const notClosable = !this.parentNode.classList.contains("closeable"); + + deselectTabList(this); + + if (notSelected || notClosable) { + selectTab(this); + const name = this.getAttribute("name"); + selectNamedTabs(name, this.id); + + if (this.classList.contains("group-tab")) { + // Persist during session + session.setItem('sphinx-tabs-last-selected', name); + } + } + + const positionAfter = this.parentNode.getBoundingClientRect().top; + const positionDelta = positionAfter - positionBefore; + // Scroll to offset content resizing + window.scrollTo(0, window.scrollY + positionDelta); +} + +/** + * Select tab and show associated panel. + * @param {Node} tab tab to select + */ +function selectTab(tab) { + tab.setAttribute("aria-selected", true); + + // Show the associated panel + document + .getElementById(tab.getAttribute("aria-controls")) + .removeAttribute("hidden"); +} + +/** + * Hide the panels associated with all tabs within the + * tablist containing this tab. + * @param {Node} tab a tab within the tablist to deselect + */ +function deselectTabList(tab) { + const parent = tab.parentNode; + const grandparent = parent.parentNode; + + Array.from(parent.children) + .forEach(t => t.setAttribute("aria-selected", false)); + + Array.from(grandparent.children) + .slice(1) // Skip tablist + .forEach(panel => panel.setAttribute("hidden", true)); +} + +/** + * Select grouped tabs with the same name, but no the tab + * with the given id. + * @param {Node} name name of grouped tab to be selected + * @param {Node} clickedId id of clicked tab + */ +function selectNamedTabs(name, clickedId=null) { + const groupedTabs = document.querySelectorAll(`.sphinx-tabs-tab[name="${name}"]`); + const tabLists = Array.from(groupedTabs).map(tab => tab.parentNode); + + tabLists + .forEach(tabList => { + // Don't want to change the tabList containing the clicked tab + const clickedTab = tabList.querySelector(`[id="${clickedId}"]`); + if (clickedTab === null ) { + // Select first tab with matching name + const tab = tabList.querySelector(`.sphinx-tabs-tab[name="${name}"]`); + deselectTabList(tab); + selectTab(tab); + } + }) +} + +if (typeof exports === 'undefined') { + exports = {}; +} + +exports.keyTabs = keyTabs; +exports.changeTabs = changeTabs; +exports.selectTab = selectTab; +exports.deselectTabList = deselectTabList; +exports.selectNamedTabs = selectNamedTabs; diff --git a/singlehtml/_static/term_role_formatting.css b/singlehtml/_static/term_role_formatting.css new file mode 100644 index 0000000..0b66095 --- /dev/null +++ b/singlehtml/_static/term_role_formatting.css @@ -0,0 +1,4 @@ +/* Make terms bold */ +a.reference span.std-term { + font-weight: bold; +} diff --git a/singlehtml/_static/togglebutton.css b/singlehtml/_static/togglebutton.css new file mode 100644 index 0000000..54a6787 --- /dev/null +++ b/singlehtml/_static/togglebutton.css @@ -0,0 +1,160 @@ +/** + * Admonition-based toggles + */ + +/* Visibility of the target */ +.admonition.toggle .admonition-title ~ * { + transition: opacity .3s, height .3s; +} + +/* Toggle buttons inside admonitions so we see the title */ +.admonition.toggle { + position: relative; +} + +/* Titles should cut off earlier to avoid overlapping w/ button */ +.admonition.toggle .admonition-title { + padding-right: 25%; + cursor: pointer; +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:hover { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 1%); +} + +/* Hovering will cause a slight shift in color to make it feel interactive */ +.admonition.toggle .admonition-title:active { + box-shadow: inset 0 0 0px 20px rgb(0 0 0 / 3%); +} + +/* Remove extra whitespace below the admonition title when hidden */ +.admonition.toggle-hidden { + padding-bottom: 0; +} + +.admonition.toggle-hidden .admonition-title { + margin-bottom: 0; +} + +/* hides all the content of a page until de-toggled */ +.admonition.toggle-hidden .admonition-title ~ * { + height: 0; + margin: 0; + opacity: 0; + visibility: hidden; +} + +/* General button style and position*/ +button.toggle-button { + /** + * Background and shape. By default there's no background + * but users can style as they wish + */ + background: none; + border: none; + outline: none; + + /* Positioning just inside the admonition title */ + position: absolute; + right: 0.5em; + padding: 0px; + border: none; + outline: none; +} + +/* Display the toggle hint on wide screens */ +@media (min-width: 768px) { + button.toggle-button.toggle-button-hidden:before { + content: attr(data-toggle-hint); /* This will be filled in by JS */ + font-size: .8em; + align-self: center; + } +} + +/* Icon behavior */ +.tb-icon { + transition: transform .2s ease-out; + height: 1.5em; + width: 1.5em; + stroke: currentColor; /* So that we inherit the color of other text */ +} + +/* The icon should point right when closed, down when open. */ +/* Open */ +.admonition.toggle button .tb-icon { + transform: rotate(90deg); +} + +/* Closed */ +.admonition.toggle button.toggle-button-hidden .tb-icon { + transform: rotate(0deg); +} + +/* With details toggles, we don't rotate the icon so it points right */ +details.toggle-details .tb-icon { + height: 1.4em; + width: 1.4em; + margin-top: 0.1em; /* To center the button vertically */ +} + + +/** + * Details-based toggles. + * In this case, we wrap elements with `.toggle` in a details block. + */ + +/* Details blocks */ +details.toggle-details { + margin: 1em 0; +} + + +details.toggle-details summary { + display: flex; + align-items: center; + cursor: pointer; + list-style: none; + border-radius: .2em; + border-left: 3px solid #1976d2; + background-color: rgb(204 204 204 / 10%); + padding: 0.2em 0.7em 0.3em 0.5em; /* Less padding on left because the SVG has left margin */ + font-size: 0.9em; +} + +details.toggle-details summary:hover { + background-color: rgb(204 204 204 / 20%); +} + +details.toggle-details summary:active { + background: rgb(204 204 204 / 28%); +} + +.toggle-details__summary-text { + margin-left: 0.2em; +} + +details.toggle-details[open] summary { + margin-bottom: .5em; +} + +details.toggle-details[open] summary .tb-icon { + transform: rotate(90deg); +} + +details.toggle-details[open] summary ~ * { + animation: toggle-fade-in .3s ease-out; +} + +@keyframes toggle-fade-in { + from {opacity: 0%;} + to {opacity: 100%;} +} + +/* Print rules - we hide all toggle button elements at print */ +@media print { + /* Always hide the summary so the button doesn't show up */ + details.toggle-details summary { + display: none; + } +} \ No newline at end of file diff --git a/singlehtml/_static/togglebutton.js b/singlehtml/_static/togglebutton.js new file mode 100644 index 0000000..215a7ee --- /dev/null +++ b/singlehtml/_static/togglebutton.js @@ -0,0 +1,187 @@ +/** + * Add Toggle Buttons to elements + */ + +let toggleChevron = ` + + + +`; + +var initToggleItems = () => { + var itemsToToggle = document.querySelectorAll(togglebuttonSelector); + console.log(`[togglebutton]: Adding toggle buttons to ${itemsToToggle.length} items`) + // Add the button to each admonition and hook up a callback to toggle visibility + itemsToToggle.forEach((item, index) => { + if (item.classList.contains("admonition")) { + // If it's an admonition block, then we'll add a button inside + // Generate unique IDs for this item + var toggleID = `toggle-${index}`; + var buttonID = `button-${toggleID}`; + + item.setAttribute('id', toggleID); + if (!item.classList.contains("toggle")){ + item.classList.add("toggle"); + } + // This is the button that will be added to each item to trigger the toggle + var collapseButton = ` + `; + + title = item.querySelector(".admonition-title") + title.insertAdjacentHTML("beforeend", collapseButton); + thisButton = document.getElementById(buttonID); + + // Add click handlers for the button + admonition title (if admonition) + admonitionTitle = document.querySelector(`#${toggleID} > .admonition-title`) + if (admonitionTitle) { + // If an admonition, then make the whole title block clickable + admonitionTitle.addEventListener('click', toggleClickHandler); + admonitionTitle.dataset.target = toggleID + admonitionTitle.dataset.button = buttonID + } else { + // If not an admonition then we'll listen for the button click + thisButton.addEventListener('click', toggleClickHandler); + } + + // Now hide the item for this toggle button unless explicitly noted to show + if (!item.classList.contains("toggle-shown")) { + toggleHidden(thisButton); + } + } else { + // If not an admonition, wrap the block in a
block + // Define the structure of the details block and insert it as a sibling + var detailsBlock = ` +
+ + ${toggleChevron} + ${toggleHintShow} + +
`; + item.insertAdjacentHTML("beforebegin", detailsBlock); + + // Now move the toggle-able content inside of the details block + details = item.previousElementSibling + details.appendChild(item) + item.classList.add("toggle-details__container") + + // Set up a click trigger to change the text as needed + details.addEventListener('click', (click) => { + let parent = click.target.parentElement; + if (parent.tagName.toLowerCase() == "details") { + summary = parent.querySelector("summary"); + details = parent; + } else { + summary = parent; + details = parent.parentElement; + } + // Update the inner text for the proper hint + if (details.open) { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintShow; + } else { + summary.querySelector("span.toggle-details__summary-text").innerText = toggleHintHide; + } + + }); + + // If we have a toggle-shown class, open details block should be open + if (item.classList.contains("toggle-shown")) { + details.click(); + } + } + }) +}; + +// This should simply add / remove the collapsed class and change the button text +var toggleHidden = (button) => { + target = button.dataset['target'] + var itemToToggle = document.getElementById(target); + if (itemToToggle.classList.contains("toggle-hidden")) { + itemToToggle.classList.remove("toggle-hidden"); + button.classList.remove("toggle-button-hidden"); + } else { + itemToToggle.classList.add("toggle-hidden"); + button.classList.add("toggle-button-hidden"); + } +} + +var toggleClickHandler = (click) => { + // Be cause the admonition title is clickable and extends to the whole admonition + // We only look for a click event on this title to trigger the toggle. + + if (click.target.classList.contains("admonition-title")) { + button = click.target.querySelector(".toggle-button"); + } else if (click.target.classList.contains("tb-icon")) { + // We've clicked the icon and need to search up one parent for the button + button = click.target.parentElement; + } else if (click.target.tagName == "polyline") { + // We've clicked the SVG elements inside the button, need to up 2 layers + button = click.target.parentElement.parentElement; + } else if (click.target.classList.contains("toggle-button")) { + // We've clicked the button itself and so don't need to do anything + button = click.target; + } else { + console.log(`[togglebutton]: Couldn't find button for ${click.target}`) + } + target = document.getElementById(button.dataset['button']); + toggleHidden(target); +} + +// If we want to blanket-add toggle classes to certain cells +var addToggleToSelector = () => { + const selector = ""; + if (selector.length > 0) { + document.querySelectorAll(selector).forEach((item) => { + item.classList.add("toggle"); + }) + } +} + +// Helper function to run when the DOM is finished +const sphinxToggleRunWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} +sphinxToggleRunWhenDOMLoaded(addToggleToSelector) +sphinxToggleRunWhenDOMLoaded(initToggleItems) + +/** Toggle details blocks to be open when printing */ +if (toggleOpenOnPrint == "true") { + window.addEventListener("beforeprint", () => { + // Open the details + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.dataset["togglestatus"] = el.open; + el.open = true; + }); + + // Open the admonitions + document.querySelectorAll(".admonition.toggle.toggle-hidden").forEach((el) => { + console.log(el); + el.querySelector("button.toggle-button").click(); + el.dataset["toggle_after_print"] = "true"; + }); + }); + window.addEventListener("afterprint", () => { + // Re-close the details that were closed + document.querySelectorAll("details.toggle-details").forEach((el) => { + el.open = el.dataset["togglestatus"] == "true"; + delete el.dataset["togglestatus"]; + }); + + // Re-close the admonition toggle buttons + document.querySelectorAll(".admonition.toggle").forEach((el) => { + if (el.dataset["toggle_after_print"] == "true") { + el.querySelector("button.toggle-button").click(); + delete el.dataset["toggle_after_print"]; + } + }); + }); +} diff --git a/singlehtml/index.html b/singlehtml/index.html new file mode 100644 index 0000000..8dac0a1 --- /dev/null +++ b/singlehtml/index.html @@ -0,0 +1,3103 @@ + + + + + + + Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
    +
  • + +
  • + Edit on GitHub +
  • +
+
+
+
+
+ +
+

Reproducible research software development using Python

+
+

Big-picture goal

+

This is a hands-on course on research software engineering. In this +workshop we assume that most workshop participants use Python in their work or +a leading a group which uses Python. Therefore, some of the examples will use +Python as the example language.

+

We will work with an example project and go through all the steps of a typical +software project. Once we have seen the building blocks, we will try to apply +them to own projects. Workshop participants will receive and also learn to give +constructive code feedback.

+
+
+

Prerequisites

+
+

Preparation

+
    +
  1. Get a GitHub account following these instructions.

  2. +
  3. You will need a text editor. If you don’t have a favorite one, we recommend +VS Code.

  4. +
  5. If you prefer to work in the terminal and not in VS Code, set up these two (skip this if you use VS Code):

    + +
  6. +
  7. One of these two software environments (if you are not sure which one to +choose or have no preference, choose Conda):

    + +
  8. +
  9. Optional and only on Linux: Apptainer following +these instructions.

  10. +
+
+
+
+

Schedule

+
+

Day 1

+ +
+
+

Day 2

+ +
+
+

Day 3

+ +
+
+

If we have time left

+ +
+
+

Conda environment

+

A Conda environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a environment.yml file that documents the dependencies:

+
name: coderefinery
+channels:
+  - conda-forge
+  - bioconda
+dependencies:
+  - python >= 3.10
+  - black
+  - click
+  - flit
+  - ipywidgets
+  - isort
+  - jupyterlab
+  - jupyterlab_code_formatter
+  - jupyterlab-git
+  - matplotlib
+  - myst-parser
+  - nbdime
+  - numpy
+  - pandas
+  - pytest
+  - pytest-cov
+  - scalene
+  - seaborn
+  - snakemake-minimal
+  - sphinx
+  - sphinx-autoapi
+  - sphinx-autobuild
+  - sphinx_rtd_theme >= 2.0
+  - vulture
+  - scikit-image
+
+
+
+
Before you create a virtual environment
+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create an environment.yml file and copy-paste the dependencies above into it.

  4. +
+
+
+
Choose the tool to manage the environment
+

If you are already using one of these tools, please continue using the tool that you like and know. +If you are new to this, we recommend using Miniconda or Miniforge.

+
    +
  • Anaconda

    +
      +
    • Advantages: easy to install, easy to use, good for beginners

    • +
    • Disadvantages: large download, installs more than we will need, license restrictions

    • +
    +
  • +
  • Miniconda

    +
      +
    • Advantages: small size, installs only what you need

    • +
    • Disadvantages: no graphical interface, license restrictions

    • +
    +
  • +
  • Miniforge

    +
      +
    • Advantages: small size, no license restrictions

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Micromamba

    +
      +
    • Advantages: fast, small size

    • +
    • Disadvantages: no graphical interface

    • +
    +
  • +
  • Pixi

    +
      +
    • Advantages: fast and new

    • +
    • Disadvantages: new and less tested and not documented here

    • +
    +
  • +
+
+
+
Creating the virtual environment
+
    +
  1. Open your terminal shell (e.g. Bash or Zsh).

  2. +
  3. Activate conda using conda activate or source ~/miniconda3/bin/activate.

  4. +
  5. Run the following command:

    +
    $ conda env create --file environment.yml
    +
    +
    +
  6. +
  7. Make sure that you see “coderefinery” in the output when you ask for a list of all available environments:

    +
    $ conda env list
    +
    +
    +
  8. +
+
+
+
+

Virtual environment

+

A virtual environment is an isolated software environment that is used to manage dependencies for a project +and you decide where it is located.

+

You will need a requirements.txt file that documents the dependencies:

+
black
+click
+flit
+ipywidgets
+isort
+jupyterlab
+jupyterlab-code-formatter
+jupyterlab-git
+matplotlib
+myst-parser
+nbdime
+numpy
+pandas
+pytest
+pytest-cov
+scalene
+seaborn
+sphinx
+sphinx-autoapi
+sphinx-autobuild
+sphinx_rtd_theme >= 2.0
+vulture
+scikit-image
+
+
+
+
Before you create a virtual environment
+
    +
  1. Create a new directory for this course.

  2. +
  3. In this directory, create a requirements.txt file and copy-paste the dependencies above into it.

  4. +
+
+
+
Creating the virtual environment
+

Now create a virtual environment in this directory either using pip and +venv +(more traditional and safer) or using uv (more modern but also less tested):

+
+

Create a new virtual environment and activate it:

+
$ python3 -m venv coderefinery-environment
+$ source coderefinery-environment/bin/activate
+
+
+

Install the dependencies into the environment:

+
$ python3 -m pip install -r requirements.txt
+
+
+
+
+
+
+
+
+

Example project: Simulating the motion of planets

+

The example code that we will study +is a hopefully simple N-body simulation written in Python. It is not important +or expected that we understand the code in any detail.

+

The big picture is that the code simulates the motion of a number of +planets:

+
    +
  • We can choose the number of planets.

  • +
  • Each planet starts with a random position, velocity, and mass.

  • +
  • At each time step, the code calculates the gravitational force between each +pair of planets.

  • +
  • The forces accelerate each planet, the acceleration modifies the velocity, +the velocity modifies the position of each planet.

  • +
  • We can choose the number of time steps.

  • +
  • The units were chosen to make numbers easy to read.

  • +
+
+
Example run
+
+

Instructor note

+

The instructor demonstrates running the code on their computer.

+
+

The code is written to accept command-line arguments to specify the number +of planets and the number of time steps.

+

We first generate starting data:

+
$ python generate-data.py --num-planets 10 --output-file initial.csv
+
+
+

The generated file (initial.csv) could look like this:

+
px,py,pz,vx,vy,vz,mass
+-46.88,-42.51,88.33,-0.86,-0.18,0.55,6.70
+-5.29,17.09,-96.13,0.66,0.45,-0.17,3.51
+83.53,-92.83,-68.77,-0.26,-0.48,0.24,6.84
+-36.31,25.48,64.16,0.85,0.75,-0.56,1.53
+-68.38,-17.21,-97.07,0.60,0.26,0.69,6.63
+-48.37,-48.74,3.92,-0.92,-0.33,-0.93,8.60
+40.53,-75.50,44.18,-0.62,-0.31,-0.53,8.04
+-27.21,10.78,-78.82,-0.09,-0.55,-0.03,5.35
+88.42,-74.95,-45.85,0.81,0.68,0.56,5.36
+39.09,53.12,-59.54,-0.54,0.56,0.07,8.98
+
+
+

Then we can simulate their motion (in this case for 20 steps):

+
$ python simulate.py --num-steps 20 \
+                     --input-file initial.csv \
+                     --output-file final.csv
+
+
+

The --output-file (final.csv) is again a CSV file (comma-separated values) +and contains the final positions of all planets.

+

It is possible to run on multiple cores and to animate the result. +Here is an example with 100 planets:

+
$ python generate-data.py --num-planets 100 --output-file initial.csv
+
+$ python simulate.py --num-steps 50 \
+                     --input-file initial.csv \
+                     --output-file final.csv \
+                     --trajectories-file trajectories.npz \
+                     --num-cores 8
+
+$ python animate.py --initial-file initial.csv \
+                    --trajectories-file trajectories.npz \
+                    --output-file animation.mp4
+
+
+
+

Learning goals

+
    +
  • What are the most important steps to make this code reusable by others +and our future selves?

  • +
  • Be able to apply these techniques to your own code/script.

  • +
+
+
+

We will not focus on …

+
    +
  • … how the code works internally in detail.

  • +
  • … whether this is the most efficient algorithm.

  • +
  • … whether the code is numerically stable.

  • +
  • … how to code scales with the number of cores.

  • +
  • … whether it is portable to other operating systems (we will discuss this later).

  • +
+
+
+
+
+

Introduction to version control with Git and GitHub

+
+
+
Motivation
+
+

Objectives

+
    +
  • Browse commits and branches of a Git repository.

  • +
  • Remember that commits are like snapshots of the repository at a certain +point in time.

  • +
  • Know the difference between Git (something that tracks changes) and +GitHub/GitLab (a platform to host Git repositories).

  • +
+
+
+
Why do we need to keep track of versions?
+

Version control is an answer to the following questions (do you recognize some +of them?):

+
    +
  • “It broke … hopefully I have a working version somewhere?”

  • +
  • “Can you please send me the latest version?”

  • +
  • “Where is the latest version?”

  • +
  • “Which version are you using?”

  • +
  • “Which version have the authors used in the paper I am trying to reproduce?”

  • +
  • “Found a bug! Since when was it there?”

  • +
  • “I am sure it used to work. When did it change?”

  • +
  • “My laptop is gone. Is my thesis now gone?”

  • +
+
+
+
Demonstration
+
    +
  • Example repository: https://github.com/workshop-material/planets

  • +
  • Commits are like snapshots and if we break something we can go back to a +previous snapshot.

  • +
  • Commits carry metadata about changes: author, date, commit message, and +a checksum.

  • +
  • Branches are like parallel universes where you can experiment with +changes without affecting the default branch: +https://github.com/workshop-material/planets/network +(“Insights” -> “Network”)

  • +
  • With version control we can annotate code +(example).

  • +
  • Collaboration: We can fork (make a copy on GitHub), clone (make a copy +to our computer), review, compare, share, and discuss.

  • +
  • Code review: Others can suggest changes using pull requests or merge +requests. These can be reviewed and discussed before they are merged. +Conceptually, they are similar to “suggesting changes” in Google Docs.

  • +
+
+
+
Features: roll-back, branching, merging, collaboration
+
    +
  • Roll-back: you can always go back to a previous version and compare

  • +
  • Branching and merging:

    +
      +
    • Work on different ideas at the same time

    • +
    • Different people can work on the same code/project without interfering

    • +
    • You can experiment with an idea and discard it if it turns out to be a bad idea

    • +
    +
  • +
+
+Branching explained with a gopher +
+

Image created using https://gopherize.me/ +(inspiration).

+
+
+ +
+
+
Talking about code
+

Which of these two is more practical?

+
    +
  1. “Clone the code, go to the file ‘simulate.py’, and search for ‘force_between_planets’. +Oh! But make sure you use the version from September 2024.”

  2. +
  3. Or I can send you a permalink: https://github.com/workshop-material/planets/blob/1343ac0/simulate.py#L31C5-L39

  4. +
+
+
+
What we typically like to snapshot
+
    +
  • Software (this is how it started but Git/GitHub can track a lot more)

  • +
  • Scripts

  • +
  • Documents (plain text files much better suitable than Word documents)

  • +
  • Manuscripts (Git is great for collaborating/sharing LaTeX or Quarto manuscripts)

  • +
  • Configuration files

  • +
  • Website sources

  • +
  • Data

  • +
+
+

Discussion

+

In this example somebody tried to keep track of versions without a version +control system tool like Git. Discuss the following directory listing. What +possible problems do you anticipate with this kind of “version control”:

+
myproject-2019.zip
+myproject-2020-february.zip
+myproject-2021-august.zip
+myproject-2023-09-19-working.zip
+myproject-2023-09-21.zip
+myproject-2023-09-21-test.zip
+myproject-2023-09-21-myversion.zip
+myproject-2023-09-21-newfeature.zip
+...
+(100 more files like these)
+
+
+ +
+
+
+
+
Forking, cloning, and browsing
+

In this episode, we will look at an existing repository to understand how +all the pieces work together. Along the way, we will make a copy (by +forking and/or cloning) of the repository for us, which will be used +for our own changes.

+
+

Objectives

+
    +
  • See a real Git repository and understand what is inside of it.

  • +
  • Understand how version control allows advanced inspection of a +repository.

  • +
  • See how Git allows multiple people to work on the same project at the same time.

  • +
  • See the big picture instead of remembering a bunch of commands.

  • +
+
+
+
GitHub, VS Code, or command line
+

We offer three different paths for this exercise:

+
    +
  • GitHub (this is the one we will demonstrate)

  • +
  • VS Code (if you prefer to follow along using an editor)

  • +
  • Command line (for people comfortable with the command line)

  • +
+
+
+
Creating a copy of the repository by “forking” or “cloning”
+

A repository is a collection of files in one directory tracked by Git. A +GitHub repository is GitHub’s copy, which adds things like access control, +issue tracking, and discussions. Each GitHub repository is owned by a user or +organization, who controls access.

+

First, we need to make our own copy of the exercise repository. This will +become important later, when we make our own changes.

+
+Illustration of forking a repository on GitHub +
+

Illustration of forking a repository on GitHub.

+
+
+
+Illustration of cloning a repository to your computer +
+

Illustration of cloning a repository to your computer.

+
+
+
+Illustration of cloning a forked repository to your computer +
+

It is also possible to do this: to clone a forked repository to your computer.

+
+
+

At all times you should be aware of if you are looking at your repository +or the upstream repository (original repository):

+
    +
  • Your repository: https://github.com/USER/planets

  • +
  • Upstream repository: https://github.com/workshop-material/planets

  • +
+
+

How to create a fork

+
    +
  1. Go to the repository view on GitHub: https://github.com/workshop-material/planets

  2. +
  3. First, on GitHub, click the button that says “Fork”. It is towards +the top-right of the screen.

  4. +
  5. You should shortly be redirected to your copy of the repository +USER/planets.

  6. +
+
+
+

Instructor note

+

Before starting the exercise session show +how to fork the repository to own account +(above).

+
+
+
+
Exercise: Copy and browse an existing project
+

Work on this by yourself or in pairs.

+
+

Exercise preparation

+
+

In this case you will work on a fork.

+

You only need to open your own view, as described above. The browser +URL should look like https://github.com/USER/planets, where +USER is your GitHub username.

+
+
+
+

Exercise: Browsing an existing project (20 min)

+

Browse the example project and +explore commits and branches, either on a fork or on a clone. Take notes and +prepare questions. The hints are for the GitHub path in the browser.

+
    +
  1. Browse the commit history: Are commit messages understandable? +(Hint: “Commit history”, the timeline symbol, above the file list)

  2. +
  3. Compare the commit history with the network graph (“Insights” -> “Network”). Can you find the branches?

  4. +
  5. Try to find the history of commits for a single file, e.g. simulate.py. +(Hint: “History” button in the file view)

  6. +
  7. Which files include the word “position”? +(Hint: the GitHub search on top of the repository view)

  8. +
  9. In the simulate.py file, find out who modified each line last and when +(click on file, then click “Blame” button). Find out who modified the gravitational constant +last and in which commit. +(Hint: “Blame” view in the file view)

  10. +
  11. Can you use this code yourself? Are you allowed to share +modifications? +(Hint: look for a license file)

  12. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+
Solution and walk-through
+
+
(1) Basic browsing
+

The most basic thing to look at is the history of commits.

+
    +
  • This is visible from a button in the repository view. We see every +change, when, and who has committed.

  • +
  • Every change has a unique identifier, such as 244c993. This can +be used to identify both this change, and the whole project’s +version as of that change.

  • +
  • Clicking on a change in the view shows more.

  • +
+
+

Click on the timeline symbol in the repository view:

+
+Screenshot on GitHub of where to find the commit history +
+
+
+
+
(2) Compare commit history with network graph
+

The commit history we saw above looks linear: one commit after +another. But if we look at the network view, we see some branches and +merges. We’ll see how to do these later. This is another one of the +basic Git views.

+
+

In a new browser tab, open the “Insights” tab, and click on “Network”. +You can hover over the commit dots to see the person who committed and +how they correspond with the commits in the other view:

+
+Screenshot on GitHub of the network graph +
+
+
+
+
(3) How can you browse the history of a single file?
+

We see the history for the whole repository, but we can also see it +for a single file.

+
+

Navigate to the file view: Main page → simulate.py. +Click the “History” button near the top right.

+
+
+
+
(4) Which files include the word “position”?
+

Version control makes it very easy to find all occurrences of a single +word. This is useful for things like finding where functions or +variables are defined or used.

+
+

We go to the main file view. We click the Search magnifying +class at the very top, type “position”, and click enter. We see every +instance, including the context.

+
+

Searching in a forked repository will not work instantaneously!

+

It usually takes a few minutes before one can search for keywords in a forked repository +since it first needs to build the search index the very first time we search. +Start it, continue with other steps, then come back to this.

+
+
+
+
+
(5) Who modified each line last and when?
+

This is called the “annotate” or “blame” view. The name “blame” +is very unfortunate, but it is the standard term for historical reasons +for this functionality and it is not meant to blame anyone.

+
+

From a file view, change preview to “Blame” towards the top-left. +To get the actual commit, click on the commit message.

+
+
+
+
(6) Can you use this code yourself? Are you allowed to share modifications?
+
    +
  • Look at the file LICENSE.

  • +
  • On GitHub, click on the file to see a nice summary of what we can do with this:

    +
    +Screenshot on GitHub summarizing license terms +
    +
  • +
+
+
+
Summary
+
    +
  • Git allowed us to understand this simple project much better than we +could, if it was just a few files on our own computer.

  • +
  • It was easy to share the project with the course.

  • +
  • By forking the repository, we created our own copy. This is +important for the following, where we will make changes to +our copy.

  • +
+
+
+
+
+
Creating branches and commits
+

The first and most basic task to do in Git is record changes using +commits. In this part, we will record changes in two +ways: on a new branch (which supports multiple lines of work at once), and directly +on the “main” branch (which happens to be the default branch here).

+
+

Objectives

+
    +
  • Record new changes to our own copy of the project.

  • +
  • Understand adding changes in two separate branches.

  • +
  • See how to compare different versions.

  • +
+
+
+
Background
+
    +
  • In the previous episode we have browsed an existing repository and saw commits +and branches.

  • +
  • Each commit is a snapshot of the entire project at a certain +point in time and has a unique identifier (hash) .

  • +
  • A branch is a line of development, and the main branch or master branch +are often the default branch in Git.

  • +
  • A branch in Git is like a sticky note that is attached to a commit. When we add +new commits to a branch, the sticky note moves to the new commit.

  • +
  • Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

  • +
+
+Branching explained with a gopher +
+

What if two people, at the same time, make two different changes? Git +can merge them together easily. Image created using https://gopherize.me/ +(inspiration).

+
+
+
+
+
Exercise: Creating branches and commits
+
+Illustration of what we want to achieve in this exercise +
+

Illustration of what we want to achieve in this exercise.

+
+
+
+

Exercise: Practice creating commits and branches (20 min)

+
    +
  1. First create a new branch and then either add a new file or modify an +existing file and commit the change. Make sure that you now work on your +copy of the example repository. In your commit you can share a Git or +programming trick you like.

  2. +
  3. In a new commit, modify the file again.

  4. +
  5. Switch to the main branch and create a commit there.

  6. +
  7. Browse the network and locate the commits that you just created (“Insights” -> “Network”).

  8. +
  9. Compare the branch that you created with the main branch. Can you find an easy way to see the differences?

  10. +
  11. Can you find a way to compare versions between two arbitrary commits in the repository?

  12. +
  13. Try to rename the branch that you created and then browse the network again.

  14. +
  15. Try to create a tag for one of the commits that you created (on GitHub, +create a “release”).

  16. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+
Solution and walk-through
+
+
(1) Create a new branch and a new commit
+
+
    +
  1. Where it says “main” at the top left, click, enter a new branch +name new-tutorial, click on the offer to create the new branch +(“Create branch new-tutorial from main”).

  2. +
  3. Make sure you are still on the new-tutorial branch (it should say +it at the top), and click “Add file” → “Create new file” from the +upper right.

  4. +
  5. Enter a filename where it says “Name your file…”.

  6. +
  7. Share some Git or programming trick you like.

  8. +
  9. Click “Commit changes”

  10. +
  11. Enter a commit message. Then click “Commit +changes”.

  12. +
+

You should appear back at the file browser view, and see your +modification there.

+
+
+
+
(2) Modify the file again with a new commit
+
+

This is similar to before, but we click on the existing file to +modify.

+
    +
  1. Click on the file you added or modified previously.

  2. +
  3. Click the edit button, the pencil icon at top-right.

  4. +
  5. Follow the “Commit changes” instructions as in the previous step.

  6. +
+
+
+
+
(3) Switch to the main branch and create a commit there
+
+
    +
  1. Go back to the main repository page (your user’s page).

  2. +
  3. In the branch switch view (top left above the file view), switch to +main.

  4. +
  5. Modify another file that already exists, following the pattern +from above.

  6. +
+
+
+
+
(4) Browse the commits you just made
+

Let’s look at what we did. Now, the main and new-tutorial branches +have diverged: both have some modifications. Try to find the commits +you created.

+
+

Insights tab → Network view (just like we have done before).

+
+
+
+
(5) Compare the branches
+

Comparing changes is an important thing we need to do. When using the +GitHub view only, this may not be so common, but we’ll show it so that +it makes sense later on.

+
+

A nice way to compare branches is to add /compare to the URL of the repository, +for example (replace USER): +https://github.com/USER/planets/compare

+
+
+
+
(6) Compare two arbitrary commits
+

This is similar to above, but not only between branches.

+
+

Following the /compare-trick above, one can compare commits on GitHub by +adjusting the following URL: +https://github.com/USER/planets/compare/VERSION1..VERSION2

+

Replace USER with your username and VERSION1 and VERSION2 with a commit hash or branch name. +Please try it out.

+
+
+
+
(7) Renaming a branch
+
+

Branch button → View all branches → three dots at right side → Rename branch.

+
+
+
+
(8) Creating a tag
+

Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

+
+

On the right side, below “Releases”, click on “Create a new release”.

+

What GitHub calls releases are actually tags in Git with additional metadata. +For the purpose of this exercise we can use them interchangeably.

+
+
+
+
+
Summary
+

In this part, we saw how we can make changes to our files. With branches, we +can track several lines of work at once, and can compare their differences.

+
    +
  • You could commit directly to main if there is only one single line +of work and it’s only you.

  • +
  • You could commit to branches if there are multiple lines of work at +once, and you don’t want them to interfere with each other.

  • +
  • Tags are useful to mark a specific commit as important, for example a +release version.

  • +
  • In Git, commits form a so-called “graph”. Branches are tags in Git function +like sticky notes that stick to specific commits. What this means for us is +that it does not cost any significant disk space to create new branches.

  • +
  • Not all files should be added to Git. For example, temporary files or +files with sensitive information or files which are generated as part of +the build process should not be added to Git. For this we use +.gitignore (more about this later: Practical advice: How much Git is necessary?).

  • +
  • Unsure on which branch you are or what state the repository is in? +On the command line, use git status frequently to get a quick overview.

  • +
+
+
+
+
Practical advice: How much Git is necessary?
+
+
Writing useful commit messages
+

Useful commit messages summarize the change and provide context.

+

If you need a commit message that is longer than one line, +then the convention is: one line summarizing the commit, then one empty line, +then paragraph(s) with more details in free form, if necessary.

+

Good example:

+
increase alpha to 2.0 for faster convergence
+
+the motivation for this change is
+to enable ...
+...
+(more context)
+...
+this is based on a discussion in #123
+
+
+
    +
  • Why something was changed is more important than what has changed.

  • +
  • Cross-reference to issues and discussions if possible/relevant.

  • +
  • Bad commit messages: “fix”, “oops”, “save work”

  • +
  • Bad examples: http://whatthecommit.com

  • +
  • Write commit messages that will be understood +15 years from now by someone else than you. Or by your future you.

  • +
  • Many projects start out as projects “just for me” and end up to be successful projects +that are developed by 50 people over decades.

  • +
  • Commits with multiple authors are possible.

  • +
+

Good references:

+ +
+

Note

+

A great way to learn how to write commit messages and to get inspired by their +style choices: browse repositories of codes that you use/like:

+

Some examples (but there are so many good examples):

+ +

When designing commit message styles consider also these:

+
    +
  • How will you easily generate a changelog or release notes?

  • +
  • During code review, you can help each other improving commit messages.

  • +
+
+

But remember: it is better to make any commit, than no commit. Especially in small projects. +Let not the perfect be the enemy of the good enough.

+
+
+
What level of branching complexity is necessary for each project?
+

Simple personal projects:

+
    +
  • Typically start with just the main branch.

  • +
  • Use branches for unfinished/untested ideas.

  • +
  • Use branches when you are not sure about a change.

  • +
  • Use tags to mark important milestones.

  • +
  • If you are unsure what to do with unfinished and not working code, commit it +to a branch.

  • +
+

Projects with few persons: you accept things breaking sometimes

+
    +
  • It might be reasonable to commit to the main branch and feature branches.

  • +
+

Projects with few persons: changes are reviewed by others

+
    +
  • You create new feature branches for changes.

  • +
  • Changes are reviewed before they are merged to the main branch.

  • +
  • Consider to write-protect the main branch so that it can only be changed +with pull requests or merge requests.

  • +
+
+
+
How large should a commit be?
+
    +
  • Better too small than too large (easier to combine than to split).

  • +
  • Often I make a commit at the end of the day (this is a unit I would not like to lose).

  • +
  • Smaller sized commits may be easier to review for others than huge commits.

  • +
  • Imperfect commits are better than no commits.

  • +
  • A commit should not contain unrelated changes to simplify review and possible +repair/adjustments/undo later (but again: imperfect commits are better than no commits).

  • +
+
+
+
Working on the command line? Use “git status” all the time
+

The git status command is one of the most useful commands in Git +to inform about which branch we are on, what we are about to commit, +which files might not be tracked, etc.

+
+
+
How about staging and committing?
+
    +
  • Commit early and often: rather create too many commits than too few. +You can always combine commits later.

  • +
  • Once you commit, it is very, very hard to really lose your code.

  • +
  • Always fully commit (or stash) before you do dangerous things, so that you know you are safe. +Otherwise it can be hard to recover.

  • +
  • Later you can start using the staging area (where you first stage and then commit in a second step).

  • +
  • Later start using git add -p and/or git commit -p.

  • +
+
+
+
What to avoid
+
    +
  • Committing generated files/directories (example: __pycache__, *.pyc) -> +use .gitignore +files +(collection of .gitignore templates).

  • +
  • Committing huge files -> use code review to detect this.

  • +
  • Committing unrelated changes together.

  • +
  • Postponing commits because the changes are “unfinished”/”ugly” -> better ugly +commits than no commits.

  • +
  • When working with branches:

    +
      +
    • Not updating your branch before starting new work.

    • +
    • Too ambitious branch which risks to never get completed.

    • +
    • Over-engineering the branch layout and safeguards in small projects.

    • +
    • Working on unrelated things on the same branch.

    • +
    +
  • +
+
+
+
+
Optional: How to turn your project to a Git repo and share it
+
+

Objectives

+
    +
  • Turn our own coding project (small or large, finished or unfinished) into a +Git repository.

  • +
  • Be able to share a repository on the web to have a backup or so that others +can reuse and collaborate or even just find it.

  • +
+
+
+
Exercise
+
+Illustration of how we turn a project into a Git repository and share it +
+

From a bunch of files to a local repository which we then share on GitHub.

+
+
+
+

Exercise: Turn your project to a Git repo and share it (20 min)

+
    +
  1. Create a new directory called myproject with one or few files in it. +This represents our own project. It is not yet a Git repository. You can try +that with your own project or use a simple placeholder example.

  2. +
  3. Turn this new directory into a Git repository.

  4. +
  5. Share this repository on GitHub (or GitLab, since it really works the same).

  6. +
+

We offer three different paths of how to do this exercise.

+
    +
  • Via GitHub web interface: easy and can be a good starting point if you are completely +new to Git.

  • +
  • VS Code is quite easy, since VS Code can offer to create the +GitHub repositories for you.

  • +
  • Command line: you need to create the +repository on GitHub and link it yourself.

  • +
+
+

Create an repository on GitHub

+

First log into GitHub, then follow the screenshots and descriptions below.

+
+Screenshot on GitHub before a new repository form is opened +
+

Click on the “plus” symbol on top right, then on “New repository”.

+
+
+

Then:

+
+Screenshot on GitHub just before a new repository is created +
+

Choose a repository name, add a short description, and in this case make sure to check “Add a +README file”. Finally “Create repository”.

+
+
+

Upload your files

+

Now that the repository is created, you can upload your files:

+
+Screenshot on GitHub just before uploading files +
+

Click on the “+” symbol and then on “Upload files”.

+
+
+
+
+
+
+
Is putting software on GitHub/GitLab/… publishing?
+

It is a good first step but to make your code truly findable and +accessible, consider making your code citable and persistent: Get a +persistent identifier (PID) such as DOI in addition to sharing the code +publicly, by using services like Zenodo or similar +services.

+
+
+
+
+
+

Code documentation

+
+

Objectives

+
    +
  • Discuss what makes good documentation.

  • +
  • Improve the README of your project or our example project.

  • +
  • Explore Sphinx which is a popular tool to build documentation websites.

  • +
  • Learn how to leverage GitHub Actions and GitHub Pages to build and deploy documentation.

  • +
+
+
+

Instructor note

+
    +
  • (30 min) Discussion

  • +
  • (30 min) Exercise: Set up a Sphinx documentation and add API documentation

  • +
  • (15 min) Demo: Building documentation with GitHub Actions

  • +
+
+
+
Why? 💗✉️ to your future self
+
    +
  • You will probably use your code in the future and may forget details.

  • +
  • You may want others to use your code or contribute +(almost impossible without documentation).

  • +
+
+
+
In-code documentation
+

Not very useful (more commentary than comment):

+
# now we check if temperature is below -50
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

More useful (explaining why):

+
# we regard temperatures below -50 degrees as measurement errors
+if temperature < -50:
+    print("ERROR: temperature is too low")
+
+
+

Keeping zombie code “just in case” (rather use version control):

+
# do not run this code!
+# if temperature > 0:
+#     print("It is warm")
+
+
+

Emulating version control:

+
# John Doe: threshold changed from 0 to 15 on August 5, 2013
+if temperature > 15:
+    print("It is warm")
+
+
+
+
+
Many languages allow “docstrings”
+

Example (Python):

+
def kelvin_to_celsius(temp_k: float) -> float:
+    """
+    Converts temperature in Kelvin to Celsius.
+
+    Parameters
+    ----------
+    temp_k : float
+        temperature in Kelvin
+
+    Returns
+    -------
+    temp_c : float
+        temperature in Celsius
+    """
+    assert temp_k >= 0.0, "ERROR: negative T_K"
+
+    temp_c = temp_k - 273.15
+
+    return temp_c
+
+
+
+

Keypoints

+
    +
  • Documentation which is only in the source code is not enough.

  • +
  • Often a README is enough.

  • +
  • Documentation needs to be kept +in the same Git repository as the code since we want it to evolve with +the code.

  • +
+
+
+
+
Often a README is enough - checklist
+
    +
  • Purpose

  • +
  • Requirements

  • +
  • Installation instructions

  • +
  • Copy-paste-able example to get started

  • +
  • Tutorials covering key functionality

  • +
  • Reference documentation (e.g. API) covering all functionality

  • +
  • Authors and recommended citation

  • +
  • License

  • +
  • Contribution guide

  • +
+

See also the +JOSS review checklist.

+
+
+
What if you need more than a README?
+ +
+
+
Diátaxis
+

Diátaxis is a systematic approach to technical documentation authoring.

+ +
+
+
Exercise: Set up a Sphinx documentation
+
+

Preparation

+

In this episode we will use the following 5 packages which we installed +previously as part of the Conda environment or Virtual environment:

+
myst-parser
+sphinx
+sphinx-rtd-theme
+sphinx-autoapi
+sphinx-autobuild
+
+
+

Which repository to use? You have 3 options:

+
    +
  • Clone your fork of the planets example repository.

  • +
  • If you don’t have that, you can clone the original repository itself: +https://github.com/workshop-material/planets

  • +
  • You can try this with your own project and the project does not have to +be a Python project.

  • +
+
+

There are at least two ways to get started with Sphinx:

+
    +
  1. Use sphinx-quickstart to create a new Sphinx project.

  2. +
  3. This is what we will do: Create three files (doc/conf.py, doc/index.md, and doc/about.md) +as starting point and improve from there.

  4. +
+
+

Exercise: Set up a Sphinx documentation

+
    +
  1. Create the following three files in your project:

    +
    planets/
    +├── doc/
    +│   ├── conf.py
    +│   ├── index.md
    +│   └── about.md
    +└── ...
    +
    +
    +

    This is conf.py:

    +
    project = "planets"
    +copyright = "2024, Authors"
    +author = "Authors"
    +release = "0.1"
    +
    +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
    +
    +extensions = [
    +    "myst_parser",  # in order to use markdown
    +]
    +
    +myst_enable_extensions = [
    +    "colon_fence",  # ::: can be used instead of ``` for better rendering
    +]
    +
    +html_theme = "sphinx_rtd_theme"
    +
    +
    +

    This is index.md (feel free to adjust):

    +
    # Our code documentation
    +
    +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
    +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
    +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
    +fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
    +culpa qui officia deserunt mollit anim id est laborum.
    +
    +:::{toctree}
    +:maxdepth: 2
    +:caption: Some caption
    +
    +about.md
    +:::
    +
    +
    +

    This is about.md (feel free to adjust):

    +
    # About this code
    +
    +Work in progress ...
    +
    +
    +
  2. +
  3. Run sphinx-build to build the HTML documentation:

    +
    $ sphinx-build doc _build
    +
    +... lots of output ...
    +The HTML pages are in _build.
    +
    +
    +
  4. +
  5. Try to open _build/index.html in your browser.

  6. +
  7. Experiment with adding more content, images, equations, code blocks, …

    + +
  8. +
+
+

There is a lot more you can do:

+
    +
  • This is useful if you want to check the integrity of all internal and external links:

    +
    $ sphinx-build doc -W -b linkcheck _build
    +
    +
    +
  • +
  • sphinx-autobuild +provides a local web server that will automatically refresh your view +every time you save a file - which makes writing and testing much easier.

  • +
+
+
+
Demo: Building documentation with GitHub Actions
+

First we need to extend the environment.yml file to include the necessary packages:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - myst-parser
+  - sphinx
+  - sphinx-rtd-theme
+  - sphinx-autoapi
+
+
+

Then we add a GitHub Actions workflow .github/workflow/sphinx.yml to build the documentation:

+
name: Build documentation
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+permissions:
+  contents: write
+
+jobs:
+  docs:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Sphinx build
+      run: |
+        sphinx-build doc _build
+      shell: bash -el {0}
+
+    - name: Deploy to GitHub Pages
+      uses: peaceiris/actions-gh-pages@v4
+      if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
+      with:
+        publish_branch: gh-pages
+        github_token: ${{ secrets.GITHUB_TOKEN }}
+        publish_dir: _build/
+        force_orphan: true
+
+
+

Now:

+
    +
  • Add these two changes to the GitHub repository.

  • +
  • Go to “Settings” -> “Pages” -> “Branch” -> gh-pages -> “Save”.

  • +
  • Look at “Actions” tab and observe the workflow running and hopefully +deploying the website.

  • +
  • Finally visit the generated site. You find it by clicking the About wheel +icon on top right of your repository. There, select “Use your GitHub Pages +website”.

  • +
  • This is how we build almost all of our lesson websites (including this one).

  • +
  • Another popular place to deploy Sphinx documentation is ReadTheDocs.

  • +
+
+
+
Optional: How to auto-generate API documentation in Python
+

Add three tiny modifications (highlighted) to doc/conf.py to auto-generate API documentation +(this requires the sphinx-autoapi package):

+
project = "planets"
+copyright = "2024, Authors"
+author = "Authors"
+release = "0.1"
+
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+extensions = [
+    "myst_parser",  # in order to use markdown
+    "autoapi.extension",  # in order to use markdown
+]
+
+# search this directory for Python files
+autoapi_dirs = [".."]
+
+# ignore this file when generating API documentation
+autoapi_ignore = ["*/conf.py"]
+
+myst_enable_extensions = [
+    "colon_fence",  # ::: can be used instead of ``` for better rendering
+]
+
+html_theme = "sphinx_rtd_theme"
+
+
+

Then rebuild the documentation (or push the changes and let GitHub rebuild it) +and you should see a new section “API Reference”.

+
+
+
Confused about reStructuredText vs. Markdown vs. MyST?
+
    +
  • At the beginning there was reStructuredText and Sphinx was built for reStructuredText.

  • +
  • Independently, Markdown was invented and evolved into a couple of flavors.

  • +
  • Markdown became more and more popular but was limited compared to reStructuredText.

  • +
  • Later, MyST +was invented to be able to write +something that looks like Markdown but in addition can do everything that +reStructuredText can do with extra directives.

  • +
+
+
+
Where to read more
+ +
+
+
+

Collaborative version control and code review

+
+
+
Concepts around collaboration
+
+

Objectives

+
    +
  • Be able to decide whether to divide work at the branch level or at the repository level.

  • +
+
+
+
Commits, branches, repositories, forks, clones
+
    +
  • repository: The project, contains all data and history (commits, branches, tags).

  • +
  • commit: Snapshot of the project, gets a unique identifier (e.g. c7f0e8bfc718be04525847fc7ac237f470add76e).

  • +
  • branch: Independent development line. The main development line is often called main.

  • +
  • tag: A pointer to one commit, to be able to refer to it later. Like a commemorative plaque +that you attach to a particular commit (e.g. phd-printed or paper-submitted).

  • +
  • cloning: Copying the whole repository to your laptop - the first time. It is not necessary to download each file one by one.

  • +
  • forking: Taking a copy of a repository (which is typically not yours) - your +copy (fork) stays on GitHub/GitLab and you can make changes to your copy.

  • +
+
+
+
Cloning a repository
+

In order to make a complete copy a whole repository, the git clone command +can be used. When cloning, all the files, of all or selected branches, of a +repository are copied in one operation. Cloning of a repository is of relevance +in a few different situations:

+
    +
  • Working on your own, cloning is the operation that you can use to create +multiple instances of a repository on, for instance, a personal computer, a +server, and a supercomputer.

  • +
  • The parent repository could be a repository that you or your colleague own. A +common use case for cloning is when working together within a smaller team +where everyone has read and write access to the same git repository.

  • +
  • Alternatively, cloning can be made from a public repository of a code that +you would like to use. Perhaps you have no intention to work on the code, but +would like to stay in tune with the latest developments, also in-between +releases of new versions of the code.

  • +
+
+Forking and cloning +
+

Forking and cloning

+
+
+
+
+
Forking a repository
+

When a fork is made on GitHub/GitLab a complete copy, of all or selected +branches, of the repository is made. The copy will reside under a different +account on GitHub/GitLab. Forking of a repository is of high relevance when +working with a git repository to which you do not have write access.

+
    +
  • In the fork repository commits can be made to the base branch (main or +master), and to other branches.

  • +
  • The commits that are made within the branches of the fork repository can be +contributed back to the parent repository by means of pull or merge requests.

  • +
+
+
+
Synchronizing changes between repositories
+
    +
  • We need a mechanism to communicate changes between the repositories.

  • +
  • We will pull or fetch updates from remote repositories (we will soon discuss the difference between pull and fetch).

  • +
  • We will push updates to remote repositories.

  • +
  • We will learn how to suggest changes within repositories on GitHub and across repositories (pull request).

  • +
  • Repositories that are forked or cloned do not automatically synchronize themselves: +We will learn how to update forks (by pulling from the “central” repository).

  • +
  • A main difference between cloning a repository and forking a repository is that the former is a general operation for generating copies of a repository to different computers, whereas forking is a particular operation implemented on GitHub/GitLab.

  • +
+
+
+
+
Collaborating within the same repository
+

In this episode, we will learn how to collaborate within the same repository. +We will learn how to cross-reference issues and pull requests, how to review +pull requests, and how to use draft pull requests.

+

This exercise will form a good basis for collaboration that is suitable for +most research groups.

+
+

Note

+

When you read or hear pull request, please think of a change proposal.

+
+
+
Exercise
+

In this exercise, we will contribute to a repository via a pull request. +This means that you propose some change, and then it is accepted (or not).

+
+

Exercise preparation

+
    +
  • First we need to get access to the exercise repository to which we will +contribute.

    +
      +
    • Instructor collects GitHub usernames from learners and adds them as collaborators to the exercise repository +(Settings -> Collaborators and teams -> Manage access -> Add people).

    • +
    +
  • +
  • Don’t forget to accept the invitation

    +
      +
    • Check https://github.com/settings/organizations/

    • +
    • Alternatively check the inbox for the email account you registered with +GitHub. GitHub emails you an invitation link, but if you don’t receive it +you can go to your GitHub notifications in the top right corner. The +maintainer can also “copy invite link” and share it within the group.

    • +
    +
  • +
  • Watching and unwatching repositories

    +
      +
    • Now that you are a collaborator, you get notified about new issues and pull +requests via email.

    • +
    • If you do not wish this, you can “unwatch” a repository (top of +the project page).

    • +
    • However, we recommend watching repositories you are interested +in. You can learn things from experts just by watching the +activity that come through a popular project.

    • +
    +
    +Unwatching a repository +
    +

    Unwatch a repository by clicking “Unwatch” in the repository view, +then “Participating and @mentions” - this way, you will get +notifications about your own interactions.

    +
    +
    +
  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements (from installation instructions):

+ +

What is familiar from the previous workshop day (not repeated here):

+
    +
  • Cloning a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Submit a pull request towards the main branch.

  • +
+

What will be new in this exercise:

+
    +
  • If you create the changes locally, you will need to push them to the remote repository.

  • +
  • Learning what a protected branch is and how to modify a protected branch: using a pull request.

  • +
  • Cross-referencing issues and pull requests.

  • +
  • Practice to review a pull request.

  • +
  • Learn about the value of draft pull requests.

  • +
+

Exercise tasks:

+
    +
  1. Start in the exercise +repository and open an +issue where you describe the change you want to make. Note down the issue +number since you will need it later.

  2. +
  3. Create a new branch.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit +cross-reference the issue you opened (see the walk-through below +for how to do that).

  6. +
  7. Push your new branch (with the new commit) to the repository you +are working on.

  8. +
  9. Open a pull request towards the main branch.

  10. +
  11. Review somebody else’s pull request and give constructive feedback. Merge their pull request.

  12. +
  13. Try to create a new branch with some half-finished work and open a draft +pull request. Verify that the draft pull request cannot be merged since it +is not meant to be merged yet.

  14. +
+
+
+
+
Solution and hints
+
+
(1) Opening an issue
+

This is done through the GitHub web interface. For example, you could +give the name of the recipe you want to add (so that others don’t add +the same one). It is the “Issues” tab.

+
+
+
(2) Create a new branch.
+

If on GitHub, you can make the branch in the web interface.

+
+
+
(3) Make a change adding the recipe
+

Add a new file with the recipe in it. Commit the file. In the commit +message, include the note about the issue number, saying that this +will close that issue.

+
+
Cross-referencing issues and pull requests
+

Each issue and each pull request gets a number and you can cross-reference them.

+

When you open an issue, note down the issue number (in this case it is #2):

+
+Each issue gets a number +
+

You can reference this issue number in a commit message or in a pull request, like in this +commit message:

+
this is the new recipe; fixes #2
+
+
+

If you forget to do that in your commit message, you can also reference the issue +in the pull request description. And instead of fixes you can also use closes or resolves +or fix or close or resolve (case insensitive).

+

Here are all the keywords that GitHub recognizes: +https://help.github.com/en/articles/closing-issues-using-keywords

+

Then observe what happens in the issue once your commit gets merged: it will +automatically close the issue and create a link between the issue and the +commit. This is very useful for tracking what changes were made in response to +which issue and to know from when until when precisely the issue was open.

+
+
+
+
(4) Push to GitHub as a new branch
+

Push the branch to the repository. You should end up with a branch +visible in the GitHub web view.

+

This is only necessary if you created the changes locally. If you created the +changes directly on GitHub, you can skip this step.

+
+

In VS Code, you can “publish the branch” to the remote repository by clicking +the cloud icon in the bottom left corner of the window:

+
+Publishing a branch in VS Code +
+
+
+
+
(5) Open a pull request towards the main branch
+

This is done through the GitHub web interface.

+
+
+
(6) Reviewing pull requests
+

You review through the GitHub web interface.

+

Checklist for reviewing a pull request:

+
    +
  • Be kind, on the other side is a human who has put effort into this.

  • +
  • Be constructive: if you see a problem, suggest a solution.

  • +
  • Towards which branch is this directed?

  • +
  • Is the title descriptive?

  • +
  • Is the description informative?

  • +
  • Scroll down to see commits.

  • +
  • Scroll down to see the changes.

  • +
  • If you get incredibly many changes, also consider the license or copyright +and ask where all that code is coming from.

  • +
  • Again, be kind and constructive.

  • +
  • Later we will learn how to suggest changes directly in the pull request.

  • +
+

If someone is new, it’s often nice to say something encouraging in the +comments before merging (even if it’s just “thanks”). If all is good +and there’s not much else to say, you could merge directly.

+
+
+
(7) Draft pull requests
+

Try to create a draft pull request:

+
+Creating a draft pull request +
+

Verify that the draft pull request cannot be merged until it is marked as ready +for review:

+
+Draft pull request cannot be merged +
+

Draft pull requests can be useful for:

+
    +
  • Feedback: You can open a pull request early to get feedback on your work without +signaling that it is ready to merge.

  • +
  • Information: They can help communicating to others that a change is coming up and in +progress.

  • +
+
+
+
What is a protected branch? And how to modify it?
+

A protected branch on GitHub or GitLab is a branch that cannot (accidentally) +deleted or force-pushed to. It is also possible to require that a branch cannot +be directly pushed to or modified, but that changes must be submitted via a +pull request.

+

To protect a branch in your own repository, go to “Settings” -> “Branches”.

+
+
+
Summary
+
    +
  • We used all the same pieces that we’ve learned previously.

  • +
  • But we successfully contributed to a collaborative project!

  • +
  • The pull request allowed us to contribute without changing directly: +this is very good when it’s not mainly our project.

  • +
+
+
+
+
+
Practicing code review
+

In this episode we will practice the code review process. We will learn how to +ask for changes in a pull request, how to suggest a change in a pull request, +and how to modify a pull request.

+

This will enable research groups to work more collaboratively and to not only +improve the code quality but also to learn from each other.

+
+
Exercise
+
+

Exercise preparation

+

We can continue in the same exercise repository which we have used in the +previous episode.

+
+
+

Exercise: Practicing code review (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • As a reviewer, we will learn how to ask for changes in a pull request.

  • +
  • As a reviewer, we will learn how to suggest a change in a pull request.

  • +
  • As a submitter, we will learn how to modify a pull request without closing +the incomplete one and opening a new one.

  • +
+

Exercise tasks:

+
    +
  1. Create a new branch and one or few commits: in these improve something but also +deliberately introduce a typo and also a larger mistake which we will want to fix during the code review.

  2. +
  3. Open a pull request towards the main branch.

  4. +
  5. As a reviewer to somebody else’s pull request, ask for an improvement and +also directly suggest a change for the small typo. (Hint: +suggestions are possible through the GitHub web interface, view of +a pull request, “Files changed” view, after selecting some lines. +Look for the “±” button.)

  6. +
  7. As the submitter, learn how to accept the suggested change. (Hint: +GitHub web interface, “Files Changed” view.)

  8. +
  9. As the submitter, improve the pull request without having to close and open +a new one: by adding a new commit to the same branch. (Hint: push +to the branch again.)

  10. +
  11. Once the changes are addressed, merge the pull request.

  12. +
+
+
+
+
Help and discussion
+

From here on out, we don’t give detailed steps to the solution. You +need to combine what you know, and the extra info below, in order to +solve the above.

+
+
How to ask for changes in a pull request
+

Technically, there are at least two common ways to ask for changes in a pull +request.

+

Either in the comment field of the pull request:

+
+Screenshot of a pull request comment field +
+

Or by using the “Review changes”:

+
+Screenshot of a pull request navigating to the "Review changes" tab +
+

And always please be kind and constructive in your comments. Remember that the +goal is not gate-keeping but collaborative learning.

+
+
+
How to suggest a change in a pull request as a reviewer
+

If you see a very small problem that is easy to fix, you can suggest a change +as a reviewer.

+

Instead of asking the submitter to tiny problem, you can suggest a change by +clicking on the plus sign next to the line number in the “Files changed” tab:

+
+Screenshot of leaving a comment to a line in a pull request +
+

Here you can comment on specific lines or even line ranges.

+

But now the interesting part is to click on the “Add a suggestion” symbol (the +one that looks like plus and minus). Now you can fix the tiny problem (in this +case a typo) and then click on the “Add single comment” button:

+
+Sequence of clicks to add a suggestion to a line in a pull request +
+

The result is this and the submitter can accept the change with a single click:

+
+Screenshot of a pull request with a suggested change +
+

After accepting with “Commit suggestion”, the improvement gets added to the +pull request.

+
+
+
How to modify a pull request to address the review comments
+

If the reviewer asks for changes, it is not necessary to close the pull request +and later open a new one. It can even be counter-productive to do so: This can +fragment the discussion and the history of the pull request and can make it +harder to understand the context of the changes.

+

A much better mechanism to recognize that pull requests are not implemented +from a specific commit to a specific branch, but always from a branch to a +branch.

+

This means that you can make amendments to the pull request by adding new +commits to the same source branch. This way the pull request will be updated +automatically and the reviewer can see the new changes and comment on them.

+

The fact that pull requests are from branch to branch also strongly suggests +that it is a good practice to create a new branch for each pull request. +Otherwise you could accidentally modify an open pull request by adding new +commits to the source branch.

+
+
+
+
Summary
+
    +
  • Our process isn’t just about code now. It’s about discussion and +working together to make the whole process better.

  • +
  • GitHub discussions and reviewing are quite powerful and can make +small changes easy.

  • +
+
+
+
+
How to contribute changes to repositories that belong to others
+

In this episode we prepare you to suggest and contribute changes to +repositories that belong to others. These might be open source projects that +you use in your work.

+

We will see how Git and services like GitHub or GitLab can be used to suggest +modification without having to ask for write access to the repository and +accept modifications without having to grant write access to others.

+
+
Exercise
+
+

Exercise preparation

+
    +
  • The exercise repository is now different: +https://github.com/workshop-material/recipe-book-forking-exercise (note the -forking-exercise).

  • +
  • First fork the exercise repository to your GitHub account.

  • +
  • Then clone your fork to your computer (if you wish to work locally).

  • +
  • Double-check that you have forked the correct repository.

  • +
+
+
+

Exercise: Collaborating within the same repository (25 min)

+

Technical requirements:

+ +

What is familiar from previous lessons:

+
    +
  • Forking a repository.

  • +
  • Creating a branch.

  • +
  • Committing a change on the new branch.

  • +
  • Opening and merging pull requests.

  • +
+

What will be new in this exercise:

+
    +
  • Opening a pull request towards the upstream repository.

  • +
  • Pull requests can be coupled with automated testing.

  • +
  • Learning that your fork can get out of date.

  • +
  • After the pull requests are merged, updating your fork with the changes.

  • +
  • Learn how to approach other people’s repositories with ideas, changes, and requests.

  • +
+

Exercise tasks:

+
    +
  1. Open an issue in the upstream exercise repository where you describe the +change you want to make. Take note of the issue number.

  2. +
  3. Create a new branch in your fork of the repository.

  4. +
  5. Make a change to the recipe book on the new branch and in the commit cross-reference the issue you opened. +See the walk-through below for how to do this.

  6. +
  7. Open a pull request towards the upstream repository.

  8. +
  9. The instructor will review and merge the pull requests. +During the review, pay attention to the automated test step (here for +demonstration purposes, we test whether the recipe contains an ingredients +and an instructions sections).

  10. +
  11. After few pull requests are merged, update your fork with the changes.

  12. +
  13. Check that in your fork you can see changes from other people’s pull requests.

  14. +
+
+
+
+
Help and discussion
+
+
Help! I don’t have permissions to push my local changes
+

Maybe you see an error like this one:

+
Please make sure you have the correct access rights
+and the repository exists.
+
+
+

Or like this one:

+
failed to push some refs to workshop-material/recipe-book-forking-exercise.git
+
+
+

In this case you probably try to push the changes not to your fork but to the original repository +and in this exercise you do not have write access to the original repository.

+

The simpler solution is to clone again but this time your fork.

+ +
+
+
Opening a pull request towards the upstream repository
+

We have learned in the previous episode that pull requests are always from +branch to branch. But the branch can be in a different repository.

+

When you open a pull request in a fork, by default GitHub will suggest to +direct it towards the default branch of the upstream repository.

+

This can be changed and it should always be verified, but in this case this is +exactly what we want to do, from fork towards upstream:

+
+Screenshot of a pull request from fork towards upstream +
+
+
+
Pull requests can be coupled with automated testing
+

We added an automated test here just for fun and so that you see that this is +possible to do.

+

In this exercise, the test is silly. It will check whether the recipe contains +both an ingredients and an instructions section.

+

In this example the test failed:

+
+Screenshot of a failed test in a pull request +
+

Click on the “Details” link to see the details of the failed test:

+
+Screenshot of details why the test failed +
+

How can this be useful?

+
    +
  • The project can define what kind of tests are expected to pass before a pull +request can be merged.

  • +
  • The reviewer can see the results of the tests, without having to run them +locally.

  • +
+

How does it work?

+
    +
  • We added a GitHub Actions workflow +to automatically run on each push or pull request towards the main branch.

  • +
+

What tests or steps can you image for your project to run automatically with +each pull request?

+
+
+
How to update your fork with changes from upstream
+

This used to be difficult but now it is two mouse clicks.

+

Navigate to your fork and notice how GitHub tells you that your fork is behind. +In my case, it is 9 commits behind upstream. To fix this, click on “Sync fork” +and then “Update branch”:

+
+Screenshot on GitHub fork page showing that the fork is behind +
+

After the update my “branch is up to date” with the upstream repository:

+
+Screenshot on GitHub after fork has been updated +
+
+
+
How to approach other people’s repositories with ideas, changes, and requests
+

Contributing very minor changes

+
    +
  • Clone or fork+clone repository

  • +
  • Create a branch

  • +
  • Commit and push change

  • +
  • Open a pull request or merge request

  • +
+

If you observe an issue and have an idea how to fix it

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • Describe the problem

  • +
  • If you have a suggestion on how to fix it, describe your suggestion

  • +
  • Possibly discuss and get feedback

  • +
  • If you are working on the fix, indicate it in the issue so that others know that somebody is working on it and who is working on it

  • +
  • Submit your fix as pull request or merge request which references/closes the issue

  • +
+
+

Motivation

+
    +
  • Inform others about an observed problem

  • +
  • Make it clear whether this issue is up for grabs or already being worked on

  • +
+
+

If you have an idea for a new feature

+
    +
  • Open an issue in the repository you wish to contribute to

  • +
  • In the issue, write a short proposal for your suggested change or new feature

  • +
  • Motivate why and how you wish to do this

  • +
  • Also indicate where you are unsure and where you would like feedback

  • +
  • Discuss and get feedback before you code

  • +
  • Once you start coding, indicate that you are working on it

  • +
  • Once you are done, submit your new feature as pull request or merge request which references/closes the issue/proposal

  • +
+
+

Motivation

+
    +
  • Get agreement and feedback before writing 5000 lines of code which might be rejected

  • +
  • If we later wonder why something was done, we have the issue/proposal as +reference and can read up on the reasoning behind a code change

  • +
+
+
+
+
+
Summary
+
    +
  • This forking workflow lets you propose changes to repositories for +which you have no write access.

  • +
  • This is the way that much modern open-source software works.

  • +
  • You can now contribute to any project you can view.

  • +
+
+
+
+
Conclict resolution, rebasing, and organizational strategies
+

(40 min demo and discussion)

+
+
+
+
+

Automated testing

+
+

Objectives

+
    +
  • Know where to start in your own project.

  • +
  • Have an example for how to make the testing part of code review.

  • +
+
+
+

Instructor note

+
    +
  • (15 min) Motivation

  • +
  • (15 min) End-to-end tests

  • +
  • (15 min) Pytest

  • +
  • (15 min) Adding the unit test to GitHub Actions

  • +
  • (10 min) What else is possible

  • +
  • (20 min) Exercise

  • +
+
+
+
Motivation
+

Testing is a way to check that the code does what it is expected to.

+
    +
  • Less scary to change code: tests will tell you whether something broke.

  • +
  • Easier for new people to join.

  • +
  • Easier for somebody to revive an old code.

  • +
  • End-to-end test: run the whole code and compare result to a reference.

  • +
  • Unit tests: test one unit (function or module). Can guide towards better +structured code: complicated code is more difficult to test.

  • +
+
+
+
How testing is often taught
+
def add(a, b):
+    return a + b
+
+
+def test_add():
+    assert add(1, 2) == 3
+
+
+

How this feels:

+
+Instruction on how to draw an owl +
+

[Citation needed]

+
+
+

Instead, we will look at and discuss a real example where we test components +from our example project.

+
+
+
Where to start
+

Short answer: Start with an end-to-end test.

+ +
+
+
End-to-end tests
+ +
+

Discussion

+

Is the end-to-end test +perfect? No. But it’s a good starting point. Discuss its limitations.

+
+
+
+
Pytest
+

First we need to add a test function, for instance +for this function:

+
def force_between_planets(position1, mass1, position2, mass2):
+    G = 1.0  # gravitational constant
+
+    r = position2 - position1
+    distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5
+    force_magnitude = G * mass1 * mass2 / distance**2
+    force = (r / distance) * force_magnitude
+
+    return force
+
+
+def test_force_between_planets():
+    position1 = np.array([0.0, 0.0, 0.0])
+    mass1 = 1.0
+    position2 = np.array([1.0, 0.0, 0.0])
+    mass2 = 2.0
+
+    force = force_between_planets(position1, mass1, position2, mass2)
+
+    assert np.allclose(force, [2.0, 0.0, 0.0])
+
+
+

Let us run the test with:

+
$ pytest simulate.py
+
+
+

Explanation: pytest will look for functions starting with test_ in files +and directories given as arguments. It will run them and report the results.

+

Now let us try this:

+
    +
  • Commit the test.

  • +
  • Break the function on purpose and run the test.

  • +
  • Does the test fail as expected?

  • +
+
+
+
Adding the unit test to GitHub Actions
+

Our next goal is that we want GitHub to run the unit test +automatically on every change.

+

First we need to extend our +environment.yml:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - pytest
+
+
+

We also need to extend .github/workflows/test.yml (highlighted line):

+
name: Test
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Run tests
+      run: |
+        ./test.sh
+        pytest simulate.py
+      shell: bash -el {0}
+
+
+

If we have time, we can try to create a pull request which would break the +code and see how the test fails.

+
+
+
What else is possible
+
    +
  • The testing above used example-based testing.

  • +
  • Test coverage: how much of the code is traversed by tests?

    + +
  • +
  • Property-based testing: generates arbitrary data matching your specification and checks that your guarantee still holds in that case.

    + +
  • +
  • Snapshot-based testing: makes it easier to generate snapshots for regression tests.

    + +
  • +
  • Mutation testing: tests pass -> change a line of code (make a mutant) -> test again and check whether all mutants get “killed”.

    + +
  • +
+
+
+
Exercises
+

Experiment with the example project and what we learned above or try it on your +own project:

+
    +
  • Add a unit test.

  • +
  • Try to run it locally.

  • +
  • Check whether it fails when you break the corresponding function.

  • +
  • Try to run it on GitHub Actions.

  • +
  • Create a pull request which would break the code and see whether the automatic test would catch it.

  • +
  • Try to design an end-to-end test for your project.

  • +
+
+
+
+

How to make the project more reusable

+

Part 1:

+
    +
  • 15 min: Motivation

  • +
  • 15 min: Best practices for managing dependencies in Python

  • +
  • 20 min: Exercise

  • +
+

Part 2:

+
    +
  • 10 min: Introduction to containers

  • +
  • 20 min: Demonstration

  • +
  • 10 min: Discussion

  • +
+
+
+

Demo: From a script towards a workflow

+

In this episode we will explore code quality and good practices in Python using +a hands-on approach. We will together build up a small project and improve it +step by step.

+

We will start from a relatively simple image processing script which can read a +telescope image of stars and our goal is to count the number of stars in +the image. Later we will want to be able to process many such images.

+

The (fictional) telescope images look like the one below here (in this +repository we can find more):

+
+Generated image representing a telescope image of stars +
+

Generated image representing a telescope image of stars.

+
+
+
+

Rough plan for this demo

+
    +
  • (15 min) Discuss how we would solve the problem, run example code, and make it work (as part of a Jupyter notebook)?

  • +
  • (15 min) Refactor the positioning code into a function and a module

  • +
  • (15 min) Now we wish to process many images - discuss how we would approach this

  • +
  • (15 min) Introduce CLI and discuss the benefits

  • +
  • (30 min) From a script to a workflow (using Snakemake)

  • +
+
+ +
+
Plan
+

Topics we wish to show and discuss:

+
    +
  • Naming (and other) conventions, project organization, modularity

  • +
  • The value of pure functions and immutability

  • +
  • Refactoring (explained through examples)

  • +
  • Auto-formatting and linting with tools like black, vulture, ruff

  • +
  • Moving a project under Git

  • +
  • How to document dependencies

  • +
  • Structuring larger software projects in a modular way

  • +
  • Command-line interfaces

  • +
  • Workflows with Snakemake

  • +
+

We will work together on the code on the big screen, and participants will be +encouraged to give suggestions and ask questions. We will end up with a Git +repository which will be shared with workshop participants.

+
+
+
Possible solutions
+ + +
+
+
+

Concepts in refactoring and modular code design

+

Topics:

+
    +
  • Pure functions

  • +
  • Design patterns: functional design vs. object-oriented design

  • +
  • How to design your code before writing it: document-driven development

  • +
+
+
+

How to release and publish your code

+

Part 1:

+
    +
  • 30 min: Overview over software licenses and code publishing

  • +
  • 15 min: We add a license, CFF file, and publish to Zenodo (sandbox)

  • +
+

Part 2:

+
    +
  • 30 min: Creating a Python package with the help of flit

  • +
  • 15 min: Deploy the package to test-PyPI using GitHub Actions

  • +
+
+
+

Profiling memory and CPU usage

+

Profilers:

+
    +
  • cProfile or profile

  • +
  • line_profiler

  • +
  • viztracer

  • +
  • scalene

  • +
  • Intel VTune

  • +
  • PyInstrument

  • +
  • py-spy

  • +
  • Pyflame

  • +
  • timeit

  • +
+

(here I will add some examples that we can profile together)

+
+
+
+
+

Credit

+

The following material (all CC-BY) was reused to create this workshop material:

+ +

(I will also summarize what changes have been made to the original material)

+
+
+
+
+
+ + +
+
+
+ +
+ +
+

© Copyright CodeRefinery team (CC-BY license).

+
+ + Built with Sphinx using a + theme + provided by Read the Docs. + + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/singlehtml/objects.inv b/singlehtml/objects.inv new file mode 100644 index 0000000..636e87a Binary files /dev/null and b/singlehtml/objects.inv differ diff --git a/testing/index.html b/testing/index.html new file mode 100644 index 0000000..1ec8c5e --- /dev/null +++ b/testing/index.html @@ -0,0 +1,379 @@ + + + + + + + Automated testing — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Automated testing

+
+

Objectives

+
    +
  • Know where to start in your own project.

  • +
  • Have an example for how to make the testing part of code review.

  • +
+
+
+

Instructor note

+
    +
  • (15 min) Motivation

  • +
  • (15 min) End-to-end tests

  • +
  • (15 min) Pytest

  • +
  • (15 min) Adding the unit test to GitHub Actions

  • +
  • (10 min) What else is possible

  • +
  • (20 min) Exercise

  • +
+
+
+

Motivation

+

Testing is a way to check that the code does what it is expected to.

+
    +
  • Less scary to change code: tests will tell you whether something broke.

  • +
  • Easier for new people to join.

  • +
  • Easier for somebody to revive an old code.

  • +
  • End-to-end test: run the whole code and compare result to a reference.

  • +
  • Unit tests: test one unit (function or module). Can guide towards better +structured code: complicated code is more difficult to test.

  • +
+
+
+

How testing is often taught

+
def add(a, b):
+    return a + b
+
+
+def test_add():
+    assert add(1, 2) == 3
+
+
+

How this feels:

+
+Instruction on how to draw an owl +
+

[Citation needed]

+
+
+

Instead, we will look at and discuss a real example where we test components +from our example project.

+
+
+

Where to start

+

Short answer: Start with an end-to-end test.

+ +
+
+

End-to-end tests

+ +
+

Discussion

+

Is the end-to-end test +perfect? No. But it’s a good starting point. Discuss its limitations.

+
+
+
+

Pytest

+

First we need to add a test function, for instance +for this function:

+
def force_between_planets(position1, mass1, position2, mass2):
+    G = 1.0  # gravitational constant
+
+    r = position2 - position1
+    distance = (r[0] ** 2 + r[1] ** 2 + r[2] ** 2) ** 0.5
+    force_magnitude = G * mass1 * mass2 / distance**2
+    force = (r / distance) * force_magnitude
+
+    return force
+
+
+def test_force_between_planets():
+    position1 = np.array([0.0, 0.0, 0.0])
+    mass1 = 1.0
+    position2 = np.array([1.0, 0.0, 0.0])
+    mass2 = 2.0
+
+    force = force_between_planets(position1, mass1, position2, mass2)
+
+    assert np.allclose(force, [2.0, 0.0, 0.0])
+
+
+

Let us run the test with:

+
$ pytest simulate.py
+
+
+

Explanation: pytest will look for functions starting with test_ in files +and directories given as arguments. It will run them and report the results.

+

Now let us try this:

+
    +
  • Commit the test.

  • +
  • Break the function on purpose and run the test.

  • +
  • Does the test fail as expected?

  • +
+
+
+

Adding the unit test to GitHub Actions

+

Our next goal is that we want GitHub to run the unit test +automatically on every change.

+

First we need to extend our +environment.yml:

+
name: planets
+channels:
+  - conda-forge
+dependencies:
+  - python=3.12
+  - numpy
+  - click
+  - matplotlib
+  - pytest
+
+
+

We also need to extend .github/workflows/test.yml (highlighted line):

+
name: Test
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout
+      uses: actions/checkout@v4
+
+    - uses: mamba-org/setup-micromamba@v1
+      with:
+        micromamba-version: '1.5.8-0' # any version from https://github.com/mamba-org/micromamba-releases
+        environment-file: environment.yml
+        init-shell: bash
+        cache-environment: true
+        post-cleanup: 'all'
+        generate-run-shell: false
+
+    - name: Run tests
+      run: |
+        ./test.sh
+        pytest simulate.py
+      shell: bash -el {0}
+
+
+

If we have time, we can try to create a pull request which would break the +code and see how the test fails.

+
+
+

What else is possible

+
    +
  • The testing above used example-based testing.

  • +
  • Test coverage: how much of the code is traversed by tests?

    + +
  • +
  • Property-based testing: generates arbitrary data matching your specification and checks that your guarantee still holds in that case.

    + +
  • +
  • Snapshot-based testing: makes it easier to generate snapshots for regression tests.

    + +
  • +
  • Mutation testing: tests pass -> change a line of code (make a mutant) -> test again and check whether all mutants get “killed”.

    + +
  • +
+
+
+

Exercises

+

Experiment with the example project and what we learned above or try it on your +own project:

+
    +
  • Add a unit test.

  • +
  • Try to run it locally.

  • +
  • Check whether it fails when you break the corresponding function.

  • +
  • Try to run it on GitHub Actions.

  • +
  • Create a pull request which would break the code and see whether the automatic test would catch it.

  • +
  • Try to design an end-to-end test for your project.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/version-control/branching-and-committing/index.html b/version-control/branching-and-committing/index.html new file mode 100644 index 0000000..2827392 --- /dev/null +++ b/version-control/branching-and-committing/index.html @@ -0,0 +1,432 @@ + + + + + + + Creating branches and commits — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Creating branches and commits

+

The first and most basic task to do in Git is record changes using +commits. In this part, we will record changes in two +ways: on a new branch (which supports multiple lines of work at once), and directly +on the “main” branch (which happens to be the default branch here).

+
+

Objectives

+
    +
  • Record new changes to our own copy of the project.

  • +
  • Understand adding changes in two separate branches.

  • +
  • See how to compare different versions.

  • +
+
+
+

Background

+
    +
  • In the previous episode we have browsed an existing repository and saw commits +and branches.

  • +
  • Each commit is a snapshot of the entire project at a certain +point in time and has a unique identifier (hash) .

  • +
  • A branch is a line of development, and the main branch or master branch +are often the default branch in Git.

  • +
  • A branch in Git is like a sticky note that is attached to a commit. When we add +new commits to a branch, the sticky note moves to the new commit.

  • +
  • Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

  • +
+
+Branching explained with a gopher +
+

What if two people, at the same time, make two different changes? Git +can merge them together easily. Image created using https://gopherize.me/ +(inspiration).

+
+
+
+
+

Exercise: Creating branches and commits

+
+Illustration of what we want to achieve in this exercise +
+

Illustration of what we want to achieve in this exercise.

+
+
+
+

Exercise: Practice creating commits and branches (20 min)

+
    +
  1. First create a new branch and then either add a new file or modify an +existing file and commit the change. Make sure that you now work on your +copy of the example repository. In your commit you can share a Git or +programming trick you like.

  2. +
  3. In a new commit, modify the file again.

  4. +
  5. Switch to the main branch and create a commit there.

  6. +
  7. Browse the network and locate the commits that you just created (“Insights” -> “Network”).

  8. +
  9. Compare the branch that you created with the main branch. Can you find an easy way to see the differences?

  10. +
  11. Can you find a way to compare versions between two arbitrary commits in the repository?

  12. +
  13. Try to rename the branch that you created and then browse the network again.

  14. +
  15. Try to create a tag for one of the commits that you created (on GitHub, +create a “release”).

  16. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+

Solution and walk-through

+
+

(1) Create a new branch and a new commit

+
+
    +
  1. Where it says “main” at the top left, click, enter a new branch +name new-tutorial, click on the offer to create the new branch +(“Create branch new-tutorial from main”).

  2. +
  3. Make sure you are still on the new-tutorial branch (it should say +it at the top), and click “Add file” → “Create new file” from the +upper right.

  4. +
  5. Enter a filename where it says “Name your file…”.

  6. +
  7. Share some Git or programming trick you like.

  8. +
  9. Click “Commit changes”

  10. +
  11. Enter a commit message. Then click “Commit +changes”.

  12. +
+

You should appear back at the file browser view, and see your +modification there.

+
+
+
+

(2) Modify the file again with a new commit

+
+

This is similar to before, but we click on the existing file to +modify.

+
    +
  1. Click on the file you added or modified previously.

  2. +
  3. Click the edit button, the pencil icon at top-right.

  4. +
  5. Follow the “Commit changes” instructions as in the previous step.

  6. +
+
+
+
+

(3) Switch to the main branch and create a commit there

+
+
    +
  1. Go back to the main repository page (your user’s page).

  2. +
  3. In the branch switch view (top left above the file view), switch to +main.

  4. +
  5. Modify another file that already exists, following the pattern +from above.

  6. +
+
+
+
+

(4) Browse the commits you just made

+

Let’s look at what we did. Now, the main and new-tutorial branches +have diverged: both have some modifications. Try to find the commits +you created.

+
+

Insights tab → Network view (just like we have done before).

+
+
+
+

(5) Compare the branches

+

Comparing changes is an important thing we need to do. When using the +GitHub view only, this may not be so common, but we’ll show it so that +it makes sense later on.

+
+

A nice way to compare branches is to add /compare to the URL of the repository, +for example (replace USER): +https://github.com/USER/planets/compare

+
+
+
+

(6) Compare two arbitrary commits

+

This is similar to above, but not only between branches.

+
+

Following the /compare-trick above, one can compare commits on GitHub by +adjusting the following URL: +https://github.com/USER/planets/compare/VERSION1..VERSION2

+

Replace USER with your username and VERSION1 and VERSION2 with a commit hash or branch name. +Please try it out.

+
+
+
+

(7) Renaming a branch

+
+

Branch button → View all branches → three dots at right side → Rename branch.

+
+
+
+

(8) Creating a tag

+

Tags are a way to mark a specific commit as important, for example a release +version. They are also like a sticky note, but they don’t move when new +commits are added.

+
+

On the right side, below “Releases”, click on “Create a new release”.

+

What GitHub calls releases are actually tags in Git with additional metadata. +For the purpose of this exercise we can use them interchangeably.

+
+
+
+
+

Summary

+

In this part, we saw how we can make changes to our files. With branches, we +can track several lines of work at once, and can compare their differences.

+
    +
  • You could commit directly to main if there is only one single line +of work and it’s only you.

  • +
  • You could commit to branches if there are multiple lines of work at +once, and you don’t want them to interfere with each other.

  • +
  • Tags are useful to mark a specific commit as important, for example a +release version.

  • +
  • In Git, commits form a so-called “graph”. Branches are tags in Git function +like sticky notes that stick to specific commits. What this means for us is +that it does not cost any significant disk space to create new branches.

  • +
  • Not all files should be added to Git. For example, temporary files or +files with sensitive information or files which are generated as part of +the build process should not be added to Git. For this we use +.gitignore (more about this later: Practical advice: How much Git is necessary?).

  • +
  • Unsure on which branch you are or what state the repository is in? +On the command line, use git status frequently to get a quick overview.

  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/version-control/browsing/index.html b/version-control/browsing/index.html new file mode 100644 index 0000000..b36d1e3 --- /dev/null +++ b/version-control/browsing/index.html @@ -0,0 +1,456 @@ + + + + + + + Forking, cloning, and browsing — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Forking, cloning, and browsing

+

In this episode, we will look at an existing repository to understand how +all the pieces work together. Along the way, we will make a copy (by +forking and/or cloning) of the repository for us, which will be used +for our own changes.

+
+

Objectives

+
    +
  • See a real Git repository and understand what is inside of it.

  • +
  • Understand how version control allows advanced inspection of a +repository.

  • +
  • See how Git allows multiple people to work on the same project at the same time.

  • +
  • See the big picture instead of remembering a bunch of commands.

  • +
+
+
+

GitHub, VS Code, or command line

+

We offer three different paths for this exercise:

+
    +
  • GitHub (this is the one we will demonstrate)

  • +
  • VS Code (if you prefer to follow along using an editor)

  • +
  • Command line (for people comfortable with the command line)

  • +
+
+
+

Creating a copy of the repository by “forking” or “cloning”

+

A repository is a collection of files in one directory tracked by Git. A +GitHub repository is GitHub’s copy, which adds things like access control, +issue tracking, and discussions. Each GitHub repository is owned by a user or +organization, who controls access.

+

First, we need to make our own copy of the exercise repository. This will +become important later, when we make our own changes.

+
+Illustration of forking a repository on GitHub +
+

Illustration of forking a repository on GitHub.

+
+
+
+Illustration of cloning a repository to your computer +
+

Illustration of cloning a repository to your computer.

+
+
+
+Illustration of cloning a forked repository to your computer +
+

It is also possible to do this: to clone a forked repository to your computer.

+
+
+

At all times you should be aware of if you are looking at your repository +or the upstream repository (original repository):

+
    +
  • Your repository: https://github.com/USER/planets

  • +
  • Upstream repository: https://github.com/workshop-material/planets

  • +
+
+

How to create a fork

+
    +
  1. Go to the repository view on GitHub: https://github.com/workshop-material/planets

  2. +
  3. First, on GitHub, click the button that says “Fork”. It is towards +the top-right of the screen.

  4. +
  5. You should shortly be redirected to your copy of the repository +USER/planets.

  6. +
+
+
+

Instructor note

+

Before starting the exercise session show +how to fork the repository to own account +(above).

+
+
+
+

Exercise: Copy and browse an existing project

+

Work on this by yourself or in pairs.

+
+

Exercise preparation

+
+

In this case you will work on a fork.

+

You only need to open your own view, as described above. The browser +URL should look like https://github.com/USER/planets, where +USER is your GitHub username.

+
+
+
+

Exercise: Browsing an existing project (20 min)

+

Browse the example project and +explore commits and branches, either on a fork or on a clone. Take notes and +prepare questions. The hints are for the GitHub path in the browser.

+
    +
  1. Browse the commit history: Are commit messages understandable? +(Hint: “Commit history”, the timeline symbol, above the file list)

  2. +
  3. Compare the commit history with the network graph (“Insights” -> “Network”). Can you find the branches?

  4. +
  5. Try to find the history of commits for a single file, e.g. simulate.py. +(Hint: “History” button in the file view)

  6. +
  7. Which files include the word “position”? +(Hint: the GitHub search on top of the repository view)

  8. +
  9. In the simulate.py file, find out who modified each line last and when +(click on file, then click “Blame” button). Find out who modified the gravitational constant +last and in which commit. +(Hint: “Blame” view in the file view)

  10. +
  11. Can you use this code yourself? Are you allowed to share +modifications? +(Hint: look for a license file)

  12. +
+
+

The solution below goes over most of the answers, and you are +encouraged to use it when the hints aren’t enough - this is by +design.

+
+
+

Solution and walk-through

+
+

(1) Basic browsing

+

The most basic thing to look at is the history of commits.

+
    +
  • This is visible from a button in the repository view. We see every +change, when, and who has committed.

  • +
  • Every change has a unique identifier, such as 244c993. This can +be used to identify both this change, and the whole project’s +version as of that change.

  • +
  • Clicking on a change in the view shows more.

  • +
+
+

Click on the timeline symbol in the repository view:

+
+Screenshot on GitHub of where to find the commit history +
+
+
+
+

(2) Compare commit history with network graph

+

The commit history we saw above looks linear: one commit after +another. But if we look at the network view, we see some branches and +merges. We’ll see how to do these later. This is another one of the +basic Git views.

+
+

In a new browser tab, open the “Insights” tab, and click on “Network”. +You can hover over the commit dots to see the person who committed and +how they correspond with the commits in the other view:

+
+Screenshot on GitHub of the network graph +
+
+
+
+

(3) How can you browse the history of a single file?

+

We see the history for the whole repository, but we can also see it +for a single file.

+
+

Navigate to the file view: Main page → simulate.py. +Click the “History” button near the top right.

+
+
+
+

(4) Which files include the word “position”?

+

Version control makes it very easy to find all occurrences of a single +word. This is useful for things like finding where functions or +variables are defined or used.

+
+

We go to the main file view. We click the Search magnifying +class at the very top, type “position”, and click enter. We see every +instance, including the context.

+
+

Searching in a forked repository will not work instantaneously!

+

It usually takes a few minutes before one can search for keywords in a forked repository +since it first needs to build the search index the very first time we search. +Start it, continue with other steps, then come back to this.

+
+
+
+
+

(5) Who modified each line last and when?

+

This is called the “annotate” or “blame” view. The name “blame” +is very unfortunate, but it is the standard term for historical reasons +for this functionality and it is not meant to blame anyone.

+
+

From a file view, change preview to “Blame” towards the top-left. +To get the actual commit, click on the commit message.

+
+
+
+

(6) Can you use this code yourself? Are you allowed to share modifications?

+
    +
  • Look at the file LICENSE.

  • +
  • On GitHub, click on the file to see a nice summary of what we can do with this:

    +
    +Screenshot on GitHub summarizing license terms +
    +
  • +
+
+
+

Summary

+
    +
  • Git allowed us to understand this simple project much better than we +could, if it was just a few files on our own computer.

  • +
  • It was easy to share the project with the course.

  • +
  • By forking the repository, we created our own copy. This is +important for the following, where we will make changes to +our copy.

  • +
+
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/version-control/index.html b/version-control/index.html new file mode 100644 index 0000000..f5e96bc --- /dev/null +++ b/version-control/index.html @@ -0,0 +1,167 @@ + + + + + + + Introduction to version control with Git and GitHub — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/version-control/motivation/index.html b/version-control/motivation/index.html new file mode 100644 index 0000000..454a972 --- /dev/null +++ b/version-control/motivation/index.html @@ -0,0 +1,283 @@ + + + + + + + Motivation — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+ +
+
+ +
+

Motivation

+
+

Objectives

+
    +
  • Browse commits and branches of a Git repository.

  • +
  • Remember that commits are like snapshots of the repository at a certain +point in time.

  • +
  • Know the difference between Git (something that tracks changes) and +GitHub/GitLab (a platform to host Git repositories).

  • +
+
+
+

Why do we need to keep track of versions?

+

Version control is an answer to the following questions (do you recognize some +of them?):

+
    +
  • “It broke … hopefully I have a working version somewhere?”

  • +
  • “Can you please send me the latest version?”

  • +
  • “Where is the latest version?”

  • +
  • “Which version are you using?”

  • +
  • “Which version have the authors used in the paper I am trying to reproduce?”

  • +
  • “Found a bug! Since when was it there?”

  • +
  • “I am sure it used to work. When did it change?”

  • +
  • “My laptop is gone. Is my thesis now gone?”

  • +
+
+
+

Demonstration

+
    +
  • Example repository: https://github.com/workshop-material/planets

  • +
  • Commits are like snapshots and if we break something we can go back to a +previous snapshot.

  • +
  • Commits carry metadata about changes: author, date, commit message, and +a checksum.

  • +
  • Branches are like parallel universes where you can experiment with +changes without affecting the default branch: +https://github.com/workshop-material/planets/network +(“Insights” -> “Network”)

  • +
  • With version control we can annotate code +(example).

  • +
  • Collaboration: We can fork (make a copy on GitHub), clone (make a copy +to our computer), review, compare, share, and discuss.

  • +
  • Code review: Others can suggest changes using pull requests or merge +requests. These can be reviewed and discussed before they are merged. +Conceptually, they are similar to “suggesting changes” in Google Docs.

  • +
+
+
+

Features: roll-back, branching, merging, collaboration

+
    +
  • Roll-back: you can always go back to a previous version and compare

  • +
  • Branching and merging:

    +
      +
    • Work on different ideas at the same time

    • +
    • Different people can work on the same code/project without interfering

    • +
    • You can experiment with an idea and discard it if it turns out to be a bad idea

    • +
    +
  • +
+
+Branching explained with a gopher +
+

Image created using https://gopherize.me/ +(inspiration).

+
+
+ +
+
+

Talking about code

+

Which of these two is more practical?

+
    +
  1. “Clone the code, go to the file ‘simulate.py’, and search for ‘force_between_planets’. +Oh! But make sure you use the version from September 2024.”

  2. +
  3. Or I can send you a permalink: https://github.com/workshop-material/planets/blob/1343ac0/simulate.py#L31C5-L39

  4. +
+
+
+

What we typically like to snapshot

+
    +
  • Software (this is how it started but Git/GitHub can track a lot more)

  • +
  • Scripts

  • +
  • Documents (plain text files much better suitable than Word documents)

  • +
  • Manuscripts (Git is great for collaborating/sharing LaTeX or Quarto manuscripts)

  • +
  • Configuration files

  • +
  • Website sources

  • +
  • Data

  • +
+
+

Discussion

+

In this example somebody tried to keep track of versions without a version +control system tool like Git. Discuss the following directory listing. What +possible problems do you anticipate with this kind of “version control”:

+
myproject-2019.zip
+myproject-2020-february.zip
+myproject-2021-august.zip
+myproject-2023-09-19-working.zip
+myproject-2023-09-21.zip
+myproject-2023-09-21-test.zip
+myproject-2023-09-21-myversion.zip
+myproject-2023-09-21-newfeature.zip
+...
+(100 more files like these)
+
+
+ +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/version-control/practical-advice/index.html b/version-control/practical-advice/index.html new file mode 100644 index 0000000..bcc49ec --- /dev/null +++ b/version-control/practical-advice/index.html @@ -0,0 +1,300 @@ + + + + + + + Practical advice: How much Git is necessary? — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Practical advice: How much Git is necessary?

+
+

Writing useful commit messages

+

Useful commit messages summarize the change and provide context.

+

If you need a commit message that is longer than one line, +then the convention is: one line summarizing the commit, then one empty line, +then paragraph(s) with more details in free form, if necessary.

+

Good example:

+
increase alpha to 2.0 for faster convergence
+
+the motivation for this change is
+to enable ...
+...
+(more context)
+...
+this is based on a discussion in #123
+
+
+
    +
  • Why something was changed is more important than what has changed.

  • +
  • Cross-reference to issues and discussions if possible/relevant.

  • +
  • Bad commit messages: “fix”, “oops”, “save work”

  • +
  • Bad examples: http://whatthecommit.com

  • +
  • Write commit messages that will be understood +15 years from now by someone else than you. Or by your future you.

  • +
  • Many projects start out as projects “just for me” and end up to be successful projects +that are developed by 50 people over decades.

  • +
  • Commits with multiple authors are possible.

  • +
+

Good references:

+ +
+

Note

+

A great way to learn how to write commit messages and to get inspired by their +style choices: browse repositories of codes that you use/like:

+

Some examples (but there are so many good examples):

+ +

When designing commit message styles consider also these:

+
    +
  • How will you easily generate a changelog or release notes?

  • +
  • During code review, you can help each other improving commit messages.

  • +
+
+

But remember: it is better to make any commit, than no commit. Especially in small projects. +Let not the perfect be the enemy of the good enough.

+
+
+

What level of branching complexity is necessary for each project?

+

Simple personal projects:

+
    +
  • Typically start with just the main branch.

  • +
  • Use branches for unfinished/untested ideas.

  • +
  • Use branches when you are not sure about a change.

  • +
  • Use tags to mark important milestones.

  • +
  • If you are unsure what to do with unfinished and not working code, commit it +to a branch.

  • +
+

Projects with few persons: you accept things breaking sometimes

+
    +
  • It might be reasonable to commit to the main branch and feature branches.

  • +
+

Projects with few persons: changes are reviewed by others

+
    +
  • You create new feature branches for changes.

  • +
  • Changes are reviewed before they are merged to the main branch.

  • +
  • Consider to write-protect the main branch so that it can only be changed +with pull requests or merge requests.

  • +
+
+
+

How large should a commit be?

+
    +
  • Better too small than too large (easier to combine than to split).

  • +
  • Often I make a commit at the end of the day (this is a unit I would not like to lose).

  • +
  • Smaller sized commits may be easier to review for others than huge commits.

  • +
  • Imperfect commits are better than no commits.

  • +
  • A commit should not contain unrelated changes to simplify review and possible +repair/adjustments/undo later (but again: imperfect commits are better than no commits).

  • +
+
+
+

Working on the command line? Use “git status” all the time

+

The git status command is one of the most useful commands in Git +to inform about which branch we are on, what we are about to commit, +which files might not be tracked, etc.

+
+
+

How about staging and committing?

+
    +
  • Commit early and often: rather create too many commits than too few. +You can always combine commits later.

  • +
  • Once you commit, it is very, very hard to really lose your code.

  • +
  • Always fully commit (or stash) before you do dangerous things, so that you know you are safe. +Otherwise it can be hard to recover.

  • +
  • Later you can start using the staging area (where you first stage and then commit in a second step).

  • +
  • Later start using git add -p and/or git commit -p.

  • +
+
+
+

What to avoid

+
    +
  • Committing generated files/directories (example: __pycache__, *.pyc) -> +use .gitignore +files +(collection of .gitignore templates).

  • +
  • Committing huge files -> use code review to detect this.

  • +
  • Committing unrelated changes together.

  • +
  • Postponing commits because the changes are “unfinished”/”ugly” -> better ugly +commits than no commits.

  • +
  • When working with branches:

    +
      +
    • Not updating your branch before starting new work.

    • +
    • Too ambitious branch which risks to never get completed.

    • +
    • Over-engineering the branch layout and safeguards in small projects.

    • +
    • Working on unrelated things on the same branch.

    • +
    +
  • +
+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/version-control/sharing/index.html b/version-control/sharing/index.html new file mode 100644 index 0000000..7621f54 --- /dev/null +++ b/version-control/sharing/index.html @@ -0,0 +1,405 @@ + + + + + + + Optional: How to turn your project to a Git repo and share it — Reproducible research software development using Python documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+ +
+
+
+
+ +
+

Optional: How to turn your project to a Git repo and share it

+
+

Objectives

+
    +
  • Turn our own coding project (small or large, finished or unfinished) into a +Git repository.

  • +
  • Be able to share a repository on the web to have a backup or so that others +can reuse and collaborate or even just find it.

  • +
+
+
+

Exercise

+
+Illustration of how we turn a project into a Git repository and share it +
+

From a bunch of files to a local repository which we then share on GitHub.

+
+
+
+

Exercise: Turn your project to a Git repo and share it (20 min)

+
    +
  1. Create a new directory called myproject with one or few files in it. +This represents our own project. It is not yet a Git repository. You can try +that with your own project or use a simple placeholder example.

  2. +
  3. Turn this new directory into a Git repository.

  4. +
  5. Share this repository on GitHub (or GitLab, since it really works the same).

  6. +
+

We offer three different paths of how to do this exercise.

+
    +
  • Via GitHub web interface: easy and can be a good starting point if you are completely +new to Git.

  • +
  • VS Code is quite easy, since VS Code can offer to create the +GitHub repositories for you.

  • +
  • Command line: you need to create the +repository on GitHub and link it yourself.

  • +
+
+

Create an repository on GitHub

+

First log into GitHub, then follow the screenshots and descriptions below.

+
+Screenshot on GitHub before a new repository form is opened +
+

Click on the “plus” symbol on top right, then on “New repository”.

+
+
+

Then:

+
+Screenshot on GitHub just before a new repository is created +
+

Choose a repository name, add a short description, and in this case make sure to check “Add a +README file”. Finally “Create repository”.

+
+
+

Upload your files

+

Now that the repository is created, you can upload your files:

+
+Screenshot on GitHub just before uploading files +
+

Click on the “+” symbol and then on “Upload files”.

+
+
+
+
+
+
+

Is putting software on GitHub/GitLab/… publishing?

+

It is a good first step but to make your code truly findable and +accessible, consider making your code citable and persistent: Get a +persistent identifier (PID) such as DOI in addition to sharing the code +publicly, by using services like Zenodo or similar +services.

+
+
+ + +
+
+ +
+
+
+
+ + + + \ No newline at end of file