├── .gitignore
├── theme
├── favicon.png
├── css
│ ├── print.css
│ ├── general.css
│ ├── variables.css
│ └── chrome.css
├── highlight.css
├── favicon.svg
├── index.hbs
├── book.js
└── highlight.js
├── src
├── assets
│ ├── logo.png
│ └── vue-chart.png
├── SUMMARY.md
├── tasks
│ ├── _index.md
│ ├── infra.md
│ ├── frontend.md
│ ├── api.md
│ ├── deploy.md
│ ├── scraper.md
│ └── db.md
└── introduction.md
├── book.toml
├── .github
└── workflows
│ └── gh-pages.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | book
2 |
--------------------------------------------------------------------------------
/theme/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/librescan-org/docs/HEAD/theme/favicon.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/librescan-org/docs/HEAD/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/vue-chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/librescan-org/docs/HEAD/src/assets/vue-chart.png
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = []
3 | title = "LibreScan - documentation"
4 | language = "en"
5 | multilingual = false
6 | src = "src"
7 |
8 | [output.html]
9 | site-url = "/docs/"
10 | git-repository-url = "https://github.com/librescan-org/docs/tree/main/"
11 | edit-url-template = "https://github.com/librescan-org/docs/edit/main/{path}"
12 |
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | - [Introduction](./introduction.md)
4 | - [LibreScan needs you!](./tasks/_index.md)
5 | - [VUEzards](./tasks/frontend.md)
6 | - [API scaffolders](./tasks/api.md)
7 | - [DB designers](./tasks/db.md)
8 | - [Scraper developers](./tasks/scraper.md)
9 | - [DevOps magicians](./tasks/deploy.md)
10 | - [Infra engineers](./tasks/infra.md)
11 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-20.04
12 | concurrency:
13 | group: ${{ github.workflow }}-${{ github.ref }}
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - name: Setup mdBook
18 | uses: peaceiris/actions-mdbook@v1
19 | with:
20 | mdbook-version: '0.4.14'
21 |
22 | - run: mdbook build
23 |
24 | - name: Deploy
25 | uses: peaceiris/actions-gh-pages@v3
26 | if: ${{ github.ref == 'refs/heads/main' }}
27 | with:
28 | github_token: ${{ secrets.GH_TOKEN }}
29 | publish_dir: ./book
30 |
--------------------------------------------------------------------------------
/theme/css/print.css:
--------------------------------------------------------------------------------
1 |
2 | #sidebar,
3 | #menu-bar,
4 | .nav-chapters,
5 | .mobile-nav-chapters {
6 | display: none;
7 | }
8 |
9 | #page-wrapper.page-wrapper {
10 | transform: none;
11 | margin-left: 0px;
12 | overflow-y: initial;
13 | }
14 |
15 | #content {
16 | max-width: none;
17 | margin: 0;
18 | padding: 0;
19 | }
20 |
21 | .page {
22 | overflow-y: initial;
23 | }
24 |
25 | code {
26 | background-color: #666666;
27 | border-radius: 5px;
28 |
29 | /* Force background to be printed in Chrome */
30 | -webkit-print-color-adjust: exact;
31 | }
32 |
33 | pre > .buttons {
34 | z-index: 2;
35 | }
36 |
37 | a, a:visited, a:active, a:hover {
38 | color: #4183c4;
39 | text-decoration: none;
40 | }
41 |
42 | h1, h2, h3, h4, h5, h6 {
43 | page-break-inside: avoid;
44 | page-break-after: avoid;
45 | }
46 |
47 | pre, code {
48 | page-break-inside: avoid;
49 | white-space: pre-wrap;
50 | }
51 |
52 | .fa {
53 | display: none !important;
54 | }
55 |
--------------------------------------------------------------------------------
/src/tasks/_index.md:
--------------------------------------------------------------------------------
1 | # LibreScan needs you!
2 |
3 | LibreScan needs talented people from the wonderful crypto community to build a truly robust service the masses can use with confidence.
4 |
5 | We compiled some sample tasks to see what kind of roles need to be filled.
6 |
7 | ## Enroll
8 |
9 | If you see yourself fit for any of the roles do not hesitate to join the movement!
10 |
11 | [Click here](https://forms.gle/92JWCf18ejMaaFSF6) to enroll as a contributor.
12 |
13 | ## What needs to be done?
14 |
15 | The following sections will give a brief overview what kind of talent the project needs:
16 |
17 | - [VUEzards](./web.md)
18 | - [API scaffolders](./api.md)
19 | - [DB designers](./db.md)
20 | - [Scraper developers](./scraper.md)
21 | - [DevOps magicians](./devops.md)
22 | - [Infra engineers](./infra.md)
23 |
24 | Read up and see how you could fit!
25 |
26 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
27 |
28 | The detailed roadmap of LibreScan will be laid out in January 2022.
29 | It is highly important that the team has a ballpark overview how many contributors we can expect!
30 |
31 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
32 |
--------------------------------------------------------------------------------
/theme/highlight.css:
--------------------------------------------------------------------------------
1 | /*
2 | * An increased contrast highlighting scheme loosely based on the
3 | * "Base16 Atelier Dune Light" theme by Bram de Haan
4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
5 | * Original Base16 color scheme by Chris Kempson
6 | * (https://github.com/chriskempson/base16)
7 | */
8 |
9 | /* Comment */
10 | .hljs-comment,
11 | .hljs-quote {
12 | color: #575757;
13 | }
14 |
15 | /* Red */
16 | .hljs-variable,
17 | .hljs-template-variable,
18 | .hljs-attribute,
19 | .hljs-tag,
20 | .hljs-name,
21 | .hljs-regexp,
22 | .hljs-link,
23 | .hljs-name,
24 | .hljs-selector-id,
25 | .hljs-selector-class {
26 | color: #d70025;
27 | }
28 |
29 | /* Orange */
30 | .hljs-number,
31 | .hljs-meta,
32 | .hljs-built_in,
33 | .hljs-builtin-name,
34 | .hljs-literal,
35 | .hljs-type,
36 | .hljs-params {
37 | color: #b21e00;
38 | }
39 |
40 | /* Green */
41 | .hljs-string,
42 | .hljs-symbol,
43 | .hljs-bullet {
44 | color: #008200;
45 | }
46 |
47 | /* Blue */
48 | .hljs-title,
49 | .hljs-section {
50 | color: #0030f2;
51 | }
52 |
53 | /* Purple */
54 | .hljs-keyword,
55 | .hljs-selector-tag {
56 | color: #9d00ec;
57 | }
58 |
59 | .hljs {
60 | display: block;
61 | overflow-x: auto;
62 | background: #f6f7f6;
63 | color: #000;
64 | padding: 0.5em;
65 | }
66 |
67 | .hljs-emphasis {
68 | font-style: italic;
69 | }
70 |
71 | .hljs-strong {
72 | font-weight: bold;
73 | }
74 |
75 | .hljs-addition {
76 | color: #22863a;
77 | background-color: #f0fff4;
78 | }
79 |
80 | .hljs-deletion {
81 | color: #b31d28;
82 | background-color: #ffeef0;
83 | }
84 |
--------------------------------------------------------------------------------
/src/tasks/infra.md:
--------------------------------------------------------------------------------
1 | # Infra engineers
2 |
3 | Managing underlying infrastructure without inflicting direct user pain is a hard task to tackle.
4 | For this very reason LibreScan makes this complication vanish as well by automating infrastructure creation by declarative means.
5 |
6 | ## Goal
7 |
8 | Support as many possible infrastructure providers as possible.
9 | Of course let's start with the most common ones like:
10 |
11 | - Amazon AWS
12 | - Google Cloud Platform
13 | - Microsoft Azure
14 |
15 | And also aim for the popular & trending choices like:
16 |
17 | - Linode
18 | - Hetzner
19 | - Scaleway
20 |
21 | ## See whether this is something for you
22 |
23 | If you can & enjoy building a Kubernetes cluster from scratch on bare metal using Terraform with a zero-config approach this is definitely your dream.
24 |
25 | [Poseidon Typhoon](https://github.com/poseidon/typhoon) is the amazing work of [Dalton Hubble](https://github.com/dghubble) which walks you through creating such a cluster powered by [Fedora CoreOS](https://typhoon.psdn.io/fedora-coreos/bare-metal/). See whether you can make it happen and whether you enjoy the process!
26 |
27 | ---
28 |
29 | ## Choice of tooling
30 |
31 | The choice here was straightforward, [Terraform](https://www.terraform.io) really shines in this field.
32 | It supports the vast majority of any possible deployment target as documented [here](https://registry.terraform.io/browse/providers).
33 |
34 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
35 |
36 | The detailed roadmap of LibreScan will be laid out in January 2022.
37 | It is highly important that the team has a ballpark overview how many contributors we can expect!
38 |
39 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
40 |
--------------------------------------------------------------------------------
/theme/favicon.svg:
--------------------------------------------------------------------------------
1 |
22 |
23 |
--------------------------------------------------------------------------------
/src/tasks/frontend.md:
--------------------------------------------------------------------------------
1 | # VUEzards
2 |
3 | First off LibreScan needs a user-friendly, intuitive and familiar web based frontend.
4 | Lowering the entry barrier for blockchain & cryptocurrency users is a key mission of LibreScan, and doing that starts with a UI which is as easy as it gets.
5 |
6 | Rule of thumb is that anything that needs a user-manual to be read is not worth using for the consumer!
7 |
8 | ---
9 |
10 | ## Choice of framework
11 |
12 | The frontend framework of choice is [Vue3](https://v3.vuejs.org).
13 |
14 | There are various reasons why Vue3 ended up as the winner here as follows:
15 |
16 | ## Masses can read & understand it
17 |
18 | If you know HTML + JS + CSS chances are you will have no trouble understanding Vue code.
19 | React & Angular are way more intrusive and use much more exotic, custom structuring & syntax.
20 |
21 | As with everything related to Blockchain, transparency is key so the more people can comfortably read it, the better.
22 |
23 | ## Flatter learning curve
24 |
25 | Again, same rule applies. Vue has a much flatter learning curve compared to React & Angular.
26 | We want to allow as many people to contribute as possible, so one plus point for Vue here as well.
27 |
28 | ## Same philosophy
29 |
30 | Vue is a truly community driven framework, whereas React & Angular are backed by Facebook & Google respectively.
31 | Many developers appreciate tools that are not backed by tech giants, and this is definitely true for the crypto community.
32 |
33 | ## Popularity, market share
34 |
35 | Philosophy is nice but when building such an important piece of an ecosystem as LibreScan, one needs to watch out for real-world trends.
36 | We did, and Vue is definitely gaining traction:
37 |
38 | 
39 |
40 | Sure GitHub stars are not a de-facto measurement, but it clearly projects tendency. The community growth confirms this as well!
41 |
42 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
43 |
44 | The detailed roadmap of LibreScan will be laid out in January 2022.
45 | It is highly important that the team has a ballpark overview how many contributors we can expect!
46 |
47 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
48 |
--------------------------------------------------------------------------------
/src/tasks/api.md:
--------------------------------------------------------------------------------
1 | # API scaffolders
2 |
3 | After inspecting data retrievable on the most popular block explorer's endpoints, a Mock API should be designed to return static data for frontend integration
4 |
5 | ## Endpoints to be inspected:
6 |
7 | - Listing entities
8 | - [List transactions](https://etherscan.io/txs)
9 | - [List blocks](https://etherscan.io/blocks)
10 | - [List all token transfers](https://etherscan.io/tokentxns)
11 | - [List specific token transfers](https://etherscan.io/token/0xaaa7a10a8ee237ea61e8ac46c50a8db8bcc1baaa) - **Note individual tabs: Transactions & Holders**
12 |
13 | - Inspecting a particular entity
14 | - [Inspect transaction](https://etherscan.io/tx/0xb90286759db02d31da762da627c6b38eb0f1e6259b7cb12c5bd8361d9e8b8fd9)
15 | - [Inspect block](https://etherscan.io/block/13608386)
16 | - [Inspect token transfer](https://etherscan.io/tx/0xe3a869af682a06a8546fa8385b28672706aeffe233dcb49325e54a0bded7c37a)
17 | - [Inspect EOA](https://etherscan.io/address/0x4dac65c769ce98d48795facbc871745ada9e49e3) - **Note individual tabs: Transactions & ERC-20 Token Txns**
18 | - [Inspect contract](https://etherscan.io/address/0xaaa7a10a8ee237ea61e8ac46c50a8db8bcc1baaa) - **Note individual tab: Contract**
19 |
20 | ---
21 |
22 | ## Choice of language
23 |
24 | LibreScan goes with JavaScript for its API component.
25 | The reasons are as follows:
26 |
27 | ### JavaScript is still the most popular language
28 |
29 | Since the API component reads from the Database and can restructure it in endless possible ways to be consumed by the frontend, it was crucial to pick a widely used language so we can have many contributors.
30 |
31 | ### Performance
32 |
33 | Python was another option considered here for above reasons, but it is still less popular and WAY less performant than NodeJS.
34 | If you want to see the actual numbers, check the popular [Benchmark game](https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/python.html) by Debian.
35 |
36 | While there are way more performant solutions than NodeJS (Go, Rust, C ...) they have way smaller communities (in descending order).
37 |
38 | Since the heavy lifting will be done by the scraper module anyway, NodeJS is a sane choice for the API component as it has to handle a very limited workflow.
39 |
40 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
41 |
42 | The detailed roadmap of LibreScan will be laid out in January 2022.
43 | It is highly important that the team has a ballpark overview how many contributors we can expect!
44 |
45 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
46 |
--------------------------------------------------------------------------------
/src/tasks/deploy.md:
--------------------------------------------------------------------------------
1 | # DevOps magicians
2 |
3 | The core of efficiency and transparency lies in proper structuring of microservices orchestrated to provide the backbone of Librescan.
4 |
5 | The default setup looks like the following:
6 |
7 | ```yaml
8 | version: '3.7'
9 |
10 | services:
11 |
12 | # THE USER FACING WEBSITE
13 | web:
14 | image: librescan/web:v0.0.1
15 | networks:
16 | - frontend
17 |
18 | # SERVES INCOMING API REQUESTS FROM THE FRONTEND
19 | api:
20 | image: librescan/api:v0.0.1
21 | volumes:
22 | - config:/config # PERMANENT CONFIG M
23 | networks:
24 | - frontend # EXPOSE AT /api URL VIA INGRESS
25 | - backend # MUST ACCESS DB READ REPLICA
26 |
27 | # SERVES AS A READ-ONLY REPLICA FOR API REQUESTS
28 | db-read:
29 | image: librescan/db:v0.0.1
30 | command: read # INSTRUCTS CONTAINER TO LAUNCH IN READ-ONLY REPLICA MODE
31 | volumes:
32 | - db-read:/var/lib/pgsql/data # MOUNT READ-HEAVY VOLUME
33 | networks:
34 | - backend # MUST ACCESS WRITABLE DB TO REPLICATE FROM
35 |
36 | # RECEIVES STRUCTURED DATA FROM SCRAPER, OFFERS REPLICATION
37 | db-write:
38 | image: librescan/db:v0.0.1
39 | command: write # INSTRUCTS CONTAINER TO LAUNCH IN WRITABLE MODE
40 | volumes:
41 | - db-write:/var/lib/pgsql/data # MOUNT WRITE-HEAVY VOLUME
42 | networks:
43 | - backend # MUST BE ACCESSIBLE FOR SCRAPER
44 |
45 | # SCRAPES DATA OFF ETH RPC, STRUCTURES IT AND WRITES IT TO DB
46 | scraper:
47 | image: librescan/scraper:v0.0.1
48 | environment:
49 | RPC: https://example.com/rpc # ETH COMPATIBLE RPC ENDPOINT
50 | networks:
51 | - backend # MUST ACCESS WRITABLE DB TO WRITE TO
52 |
53 | networks:
54 |
55 | # THE FRONTEND NETWORK IS CONNECTED TO INGRESS CTL
56 | frontend:
57 | driver: bridge
58 |
59 | # BACKEND NETWORK IS FOR BACKEND SVC ONLY
60 | backend:
61 | driver: bridge
62 |
63 | volumes:
64 |
65 | config: # VOLUME FOR PERSISTENT CONFIGURATION
66 | db-read: # RESIDES ON SEPARATE READ-HEAVY TUNED DRIVE
67 | db-write: # RESIDES ON SEPARATE WRITE-HEAVY TUNED DRIVE
68 | ```
69 |
70 | We are looking for DevOps wizards responsible for fine-tuning the microservice structure.
71 |
72 | The first requirement is to translate above docker-compose manifest file into various Kubernetes manifests (deployments, services, ingresses, PV & PVCs, etc.)
73 |
74 | ---
75 |
76 | ## Choice of orchestrator(s)
77 |
78 | We have to support everything here really.
79 | This is not a subjective question since this part of the application is vastly different from other components.
80 | The other modules can be chosen by us developers, but we surely must not force users to adopt to new container orchestration systems.
81 |
82 | ### Deployment targets to be supported
83 |
84 | - [Kubernetes](https://kubernetes.io)
85 | - [Docker Swarm](https://docs.docker.com/engine/swarm/)
86 | - [Docker Compose](https://docs.docker.com/compose/)
87 |
88 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
89 |
90 | The detailed roadmap of LibreScan will be laid out in January 2022.
91 | It is highly important that the team has a ballpark overview how many contributors we can expect!
92 |
93 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LibreScan
2 |
3 | ## A truly FREE, decentralized blockchain explorer
4 |
5 | The LibreScan team is looking for developers contributing to developing of a novel, openly accessible and truly free blockchain explorer.
6 |
7 | ## Problem
8 |
9 | There is an eager need for a non-centralized, easily self-hostable & user friendly explorer, since while (most) blockchains are properly decentralized, 99% of users interact with them using completely centralized UIs.
10 |
11 | The most often used type of such an evil UI is the block explorer itself.
12 |
13 | Main issue is going 100% against blockchains' decentralization principles, as users are effectively associating all their valuable information (their wallet addresses, balances, owned token addresses etc.) with their IP addresses as soon as they make a lookup for them through such centralized explorers.
14 |
15 | Even worse is that if any organization would ever decide to censor / ban blockchains and cryptocurrencies, they could be very effective by cutting off access to these very services.
16 | Imagine a community highly reliant on these explorers waking up to a day where non of their points of trust is reachable.
17 |
18 | This brings the next problem, which is trust itself. Whatever data these centralized services display, the users believe. Another important philosophy cornerstone of blockchain is the "never trust, always verify". Well, the centralized explorers go against this principle 100% as well by giving users a false comfort of exposing blockchain data that is supposedly verified. They could easily showcase false data and 99% of the users would believe it, since they have no verification tooling!
19 |
20 | ## Solution
21 |
22 | All above issues can be solved by providing a truly free & open-source solution for this usecase. Companies have been profiteering way too long already of selling user data (blockchain data <-> personal metadata mappings) putting members of our community at risk!
23 |
24 | The goal is to give the blockchain community an easily self hostable solution making the UI part just as much decentralized as blockchains themselves are.
25 |
26 | As a result data meant to kept private stays private indeed.
27 |
28 | ## Quick overview of components
29 |
30 | | Module | Lang / Tech | Purpose |
31 | | --------------------------- | ----------- | --------------------------------------------------- |
32 | | [**frontend**](https://github.com/librescan-org/frontend) [[docs]](https://librescan-org.github.io/docs/tasks/frontend) | Vue3 | Responsible web frontend |
33 | | [**api**](https://github.com/librescan-org/api) [[docs]](https://librescan-org.github.io/docs/tasks/api) | NodeJS | Retrieves and serves chain data from DB to frontend |
34 | | [**db**](https://github.com/librescan-org/db) [[docs]](https://librescan-org.github.io/docs/tasks/db) | PostgreSQL | Stores restructured chain data from scraper |
35 | | [**scraper**](https://github.com/librescan-org/scraper) [[docs]](https://librescan-org.github.io/docs/tasks/scraper) | Go | Scrapes & structures blockchain data |
36 | | [**deploy**](https://github.com/librescan-org/deploy) [[docs]](https://librescan-org.github.io/docs/tasks/deploy) | Various | Orchestration platform manifests |
37 | | [**infra**](https://github.com/librescan-org/infra) [[docs]](https://librescan-org.github.io/docs/tasks/infra) | Terraform | Infrastructure as code for popular platforms |
38 |
39 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
40 |
41 | The detailed roadmap of LibreScan will be laid out in January 2022.
42 | It is highly important that the team has a ballpark overview how many contributors we can expect!
43 |
44 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
45 |
--------------------------------------------------------------------------------
/src/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 |
3 | The LibreScan team is looking for developers contributing to developing of a novel, openly accessible and truly free blockchain explorer.
4 |
5 | ## Problem
6 |
7 | There is an eager need for a non-centralized, easily self-hostable & user friendly explorer, since while (most) blockchains are properly decentralized, 99% of users interact with them using completely centralized UIs.
8 |
9 | The most often used type of such an evil UI is the block explorer itself.
10 |
11 | Main issue is going 100% against blockchains' decentralization principles, as users are effectively associating all their valuable information (their wallet addresses, balances, owned token addresses etc.) with their IP addresses as soon as they make a lookup for them through such centralized explorers.
12 |
13 | Even worse is that if any organization would ever decide to censor / ban blockchains and cryptocurrencies, they could be very effective by cutting off access to these very services.
14 | Imagine a community highly reliant on these explorers waking up to a day where non of their points of trust is reachable.
15 |
16 | This brings the next problem, which is trust itself. Whatever data these centralized services display, the users believe. Another important philosophy cornerstone of blockchain is the "never trust, always verify". Well, the centralized explorers go against this principle 100% as well by giving users a false comfort of exposing blockchain data that is supposedly verified. They could easily showcase false data and 99% of the users would believe it, since they have no verification tooling!
17 |
18 | ## Solution
19 |
20 | All above issues can be solved by providing a truly free & open-source solution for this usecase. Companies have been profiteering way too long already of selling user data (blockchain data <-> personal metadata mappings) putting members of our community at risk!
21 |
22 | The goal is to give the blockchain community an easily self hostable solution making the UI part just as much decentralized as blockchains themselves are.
23 |
24 | As a result data meant to kept private stays private indeed.
25 |
26 | ## Quick overview of components
27 |
28 | | Module | Lang / Tech | Purpose |
29 | | --------------------------- | ----------- | --------------------------------------------------- |
30 | | [**frontend**](https://librescan-org.github.io/docs/tasks/frontend) [[docs]](https://librescan-org.github.io/docs/tasks/frontend) | Vue3 | Responsible web frontend |
31 | | [**api**](https://librescan-org.github.io/docs/tasks/api) [[docs]](https://librescan-org.github.io/docs/tasks/api) | NodeJS | Retrieves and serves chain data from DB to frontend |
32 | | [**db**](https://librescan-org.github.io/docs/tasks/db) [[docs]](https://librescan-org.github.io/docs/tasks/db) | PostgreSQL | Stores restructured chain data from scraper |
33 | | [**scraper**](https://librescan-org.github.io/docs/tasks/scraper) [[docs]](https://librescan-org.github.io/docs/tasks/scraper) | Go | Scrapes & structures blockchain data |
34 | | [**deploy**](https://librescan-org.github.io/docs/tasks/deploy) [[docs]](https://librescan-org.github.io/docs/tasks/deploy) | Various | Orchestration platform manifests |
35 | | [**infra**](https://librescan-org.github.io/docs/tasks/infra) [[docs]](https://librescan-org.github.io/docs/tasks/infra) | Terraform | Infrastructure as code for popular platforms |
36 |
37 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
38 |
39 | The detailed roadmap of LibreScan will be laid out in January 2022.
40 | It is highly important that the team has a ballpark overview how many contributors we can expect!
41 |
42 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
43 |
--------------------------------------------------------------------------------
/src/tasks/scraper.md:
--------------------------------------------------------------------------------
1 | # Scraper developers
2 |
3 | After the DB has been constructed and the API is ready to serve data from it to the frontend, we need to start filling the DB with real world data scraped off a real blockchain.
4 |
5 | The simple hierarchy is that each block contains N transactions. Transactions can be of various kinds, respectively:
6 |
7 | ## Regular ETH transaction
8 |
9 | This is the simplest form of a transaction, where the particular chain's native currency (ETH on Ethereum, BNB on Binance Smart Chain, etc.) gets transferred from address 0xA to address 0xB.
10 |
11 | They can be easily recognized by the scraper as all these transactions have an empty data field (0x).
12 |
13 | ## Contract deployment transaction
14 |
15 | Contract deployment transactions can be also detected without much effort, since these are the only transactions where there is no recipient defined.
16 |
17 | Any transaction where there is no recipient and the data field is not empty is definitely a contract deployment transaction.
18 |
19 | We need to further differentiate token contract and other custom contract deployments:
20 |
21 | - ERC20 Token contract
22 |
23 | Such a contract implements the [EIP-20 Token Standard Inferface](https://eips.ethereum.org/EIPS/eip-20) and has the specified method IDs present in the contract bytecode.
24 |
25 | - Other custom contract
26 |
27 | Anything which is not an ERC20 token contract falls into this category, meaning if at least ONE of the method signatures present in EIP20 is not defined in the contract then it falls into this category.
28 |
29 | ## Token transfer transaction
30 |
31 | These kind of transactions contain information about a token transaction in their either their data field or emit a Transfer(address, address, uint256) type of event during execution
32 |
33 | - Direct transfers
34 |
35 | These kinds of transfers are easily recognizable from the data field's first four bytes being ```0xa9059cbb``` indicating the transfer(address, uint256) function signature.
36 |
37 | Two 32byte parameters will follow, the first being trimmed to 20bytes which indicates the recipient of the transfer, and the other value meaning the amount of tokens transferred in a HEX encoded form.
38 |
39 | - Internal transfers
40 |
41 | When some other kind of transaction (e.g. DEX trade) moves tokens, they will event a single or multiple Transfer(address, address, uint256) signature events. These events should be parsed if they are emitted and inserted into the DB.
42 |
43 | ---
44 |
45 | ## Choice of language
46 |
47 | The scraper module of LibreScan is implemented in [Go](https://go.dev).
48 |
49 | ### Ecosystem
50 |
51 | Go is a quite popular programming language in the blockchain & crypto ecosystem, as major players build their official engines with it.
52 | Just to name a few:
53 |
54 | - [Ethereum](https://github.com/ethereum/go-ethereum)
55 | - [Binance](https://github.com/binance-chain/bsc)
56 | - [Coinbase](https://github.com/coinbase/kryptology)
57 |
58 | ### Language purpose
59 |
60 | While it is true that Rust is more performant than Go, but the Go language was especially designed for concurrent network based applications
61 |
62 | ### Learning curve
63 |
64 | Again, the learning curve (=entry barrier) was an important factor as well when making the decision.
65 | Rust has a way steeper learning curve and has unquestionably harder to understand sources. Same philosophy applies that the more people are able to read how stuff works, the better.
66 |
67 | ### Considering benefits & tradeoffs
68 |
69 | Comparing all the above benefits of Go with the performance tradeoff against Rust, the choice was a no-brainer.
70 | If you love Go & crypto you should really join the development to build something truly magnificent for this space!
71 |
72 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
73 |
74 | The detailed roadmap of LibreScan will be laid out in January 2022.
75 | It is highly important that the team has a ballpark overview how many contributors we can expect!
76 |
77 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
78 |
--------------------------------------------------------------------------------
/src/tasks/db.md:
--------------------------------------------------------------------------------
1 | # DB designers
2 |
3 | Librescan stack will utilize a PostgreSQL database to store blockchain data in the most storage efficient way without compromising on lookup speeds.
4 |
5 | It must be able to perform well even on a Raspberry Pi 4 with two attached SSD drives.
6 |
7 | The goal is to turn the following YAML draft into PostgreSQL table creation statements:
8 |
9 | ```yaml
10 | ids:
11 | id: bigserial
12 | idx: bytea # 16 bytes, indexed, used for lookup
13 | remainder: bytea # remainder of binary data
14 | type: smallint # 0=eoa 1=contract 2=txhash 3=blockhash
15 |
16 | nicks:
17 | id: bigint # foreign key id@ids
18 | nick: string # nickname of the particular id
19 | type: smallint # indicates nickname type
20 |
21 | erc20tokens:
22 | id: bigint # foreign key id@ids
23 | name: string # token name
24 | symbol: string # token symbol
25 | decimals: smallint # how many decimals the token has
26 | supply: bigint # divided by decimals
27 |
28 | blocks:
29 | id: bigint # foreign key id@ids, the blockhash
30 | height: int # block height in the chain
31 | created_at: int # unix ts
32 |
33 | txs:
34 | txhash: bigint # foreign key id@ids, the txhash
35 | block: int # block height, foreign key height@blocks
36 | value: bigint # in gwei
37 | from_id: bigint # foreign key id@ids
38 | to_id: bigint # foreign key id@ids
39 | gas_limit: bigint
40 | gas_price: bigint
41 | method_id: int? # 4 bytes
42 | params: bytea? # not contract deployment, not erc20 tx
43 |
44 | ethtxs: # if data == 0x tx gets inserted
45 | txhash: bigint # foreign key id@ids
46 | from_id: bigint # foreign key id@ids
47 | to_id: bigint # foreign key id@ids
48 | value: bigint # in gwei
49 |
50 | erc20txs: # if first four bytes indicate transfer signature tx gets inserted
51 | txhash: bigint # foreign key id@ids
52 | token_id: bigint # foreign key id@ids
53 | from_id: bigint # foreign key id@ids
54 | to_id: bigint # foreign key id@ids
55 | value: bigint # divided by decimals
56 |
57 | contracts:
58 | txhash: bigint # foreign key id@ids
59 | address_id: bigint # contract address, foreign key id@ids
60 | deployer_id: bigint # deployer address, foreign key id@ids
61 | bytecode: bytea # contract bytecode
62 |
63 | stats:
64 | address_id: bigint # foreign key id@ids
65 | token_id: bigint # foreign key id@ids, or 0 for eth transfers
66 | balance: bigint # divided by decimals / gwei
67 | first_in: int # unix ts
68 | first_out: int # unix ts
69 | last_in: int # unix ts
70 | last_out: int # unix ts
71 |
72 | ```
73 |
74 | Constructive comments to the structure are welcome as well.
75 |
76 | ---
77 |
78 | ## Choice of engine
79 |
80 | PostgreSQL was chosen for several reasons as follows:
81 |
82 | ### Performance & Scaling
83 |
84 | PostgreSQL is usually the choice for open-source projects of this magnitude.
85 | The community around it is also preferable in our humble opinion.
86 |
87 | > If you’re developing an application with a database back end, which of the two should you use? Consider PostgreSQL for any application that might grow to enterprise scope, with complex queries and frequent write operations. If you’re new to the world of databases and don’t expect your application to scale up, or you’re looking for a quick tool for prototyping, then consider MySQL.
88 |
89 | ### Philosopy
90 |
91 | Again, the Philisophy of the technologies used is another important factor as we've seen when we described the previous modules. Consider the following:
92 |
93 | > One of the original developers of Ingres returned to Berkeley in 1985 (after founding a company that commercialized Ingres) to develop a successor to Ingres that he named Postgres. The name was officially changed to PostgreSQL to take advantage of the reference to Structured Query Language, but the project uses both names. The first production release, PostgreSQL 6.0, came out in 1997. Now at version 14 (beta), Postgres is developed by an “unincorporated association of volunteers and companies who share code under the PostgreSQL Licence,” according to a project FAQ.
94 |
95 | > Unlike PostgreSQL, MySQL has always been under corporate control. Original developer MySQL AB was acquired by Sun Microsystems in 2008, shortly before Sun was itself acquired by Oracle in 2010.
96 |
97 | *Source from [fivetran.com](https://www.fivetran.com/blog/postgresql-vs-mysql)*
98 |
99 | # [> register <](https://forms.gle/92JWCf18ejMaaFSF6) as a contributor
100 |
101 | The detailed roadmap of LibreScan will be laid out in January 2022.
102 | It is highly important that the team has a ballpark overview how many contributors we can expect!
103 |
104 | If you share the same mindset and you are willing to add value to the crypto community sign up via [this link](https://forms.gle/92JWCf18ejMaaFSF6).
105 |
--------------------------------------------------------------------------------
/theme/css/general.css:
--------------------------------------------------------------------------------
1 | /* Base styles and content styles */
2 |
3 | @import 'variables.css';
4 |
5 | :root {
6 | /* Browser default font-size is 16px, this way 1 rem = 10px */
7 | font-size: 62.5%;
8 | }
9 |
10 | html {
11 | font-family: "Open Sans", sans-serif;
12 | color: var(--fg);
13 | background-color: var(--bg);
14 | text-size-adjust: none;
15 | }
16 |
17 | body {
18 | margin: 0;
19 | font-size: 1.6rem;
20 | overflow-x: hidden;
21 | }
22 |
23 | code {
24 | font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important;
25 | font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
26 | }
27 |
28 | /* Don't change font size in headers. */
29 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
30 | font-size: unset;
31 | }
32 |
33 | .left { float: left; }
34 | .right { float: right; }
35 | .boring { opacity: 0.6; }
36 | .hide-boring .boring { display: none; }
37 | .hidden { display: none !important; }
38 |
39 | h2, h3 { margin-top: 2.5em; }
40 | h4, h5 { margin-top: 2em; }
41 |
42 | .header + .header h3,
43 | .header + .header h4,
44 | .header + .header h5 {
45 | margin-top: 1em;
46 | }
47 |
48 | h1:target::before,
49 | h2:target::before,
50 | h3:target::before,
51 | h4:target::before,
52 | h5:target::before,
53 | h6:target::before {
54 | display: inline-block;
55 | content: "»";
56 | margin-left: -30px;
57 | width: 30px;
58 | }
59 |
60 | /* This is broken on Safari as of version 14, but is fixed
61 | in Safari Technology Preview 117 which I think will be Safari 14.2.
62 | https://bugs.webkit.org/show_bug.cgi?id=218076
63 | */
64 | :target {
65 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
66 | }
67 |
68 | .page {
69 | outline: 0;
70 | padding: 0 var(--page-padding);
71 | margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
72 | }
73 | .page-wrapper {
74 | box-sizing: border-box;
75 | }
76 | .js:not(.sidebar-resizing) .page-wrapper {
77 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */
78 | }
79 |
80 | .content {
81 | overflow-y: auto;
82 | padding: 0 15px;
83 | padding-bottom: 50px;
84 | }
85 | .content main {
86 | margin-left: auto;
87 | margin-right: auto;
88 | max-width: var(--content-max-width);
89 | }
90 | .content p { line-height: 1.45em; }
91 | .content ol { line-height: 1.45em; }
92 | .content ul { line-height: 1.45em; }
93 | .content a { text-decoration: none; }
94 | .content a:hover { text-decoration: underline; }
95 | .content img, .content video { max-width: 100%; }
96 | .content .header:link,
97 | .content .header:visited {
98 | color: var(--fg);
99 | }
100 | .content .header:link,
101 | .content .header:visited:hover {
102 | text-decoration: none;
103 | }
104 |
105 | table {
106 | margin: 0 auto;
107 | border-collapse: collapse;
108 | }
109 | table td {
110 | padding: 3px 20px;
111 | border: 1px var(--table-border-color) solid;
112 | }
113 | table thead {
114 | background: var(--table-header-bg);
115 | }
116 | table thead td {
117 | font-weight: 700;
118 | border: none;
119 | }
120 | table thead th {
121 | padding: 3px 20px;
122 | }
123 | table thead tr {
124 | border: 1px var(--table-header-bg) solid;
125 | }
126 | /* Alternate background colors for rows */
127 | table tbody tr:nth-child(2n) {
128 | background: var(--table-alternate-bg);
129 | }
130 |
131 |
132 | blockquote {
133 | margin: 20px 0;
134 | padding: 0 20px;
135 | color: var(--fg);
136 | background-color: var(--quote-bg);
137 | border-top: .1em solid var(--quote-border);
138 | border-bottom: .1em solid var(--quote-border);
139 | }
140 |
141 |
142 | :not(.footnote-definition) + .footnote-definition,
143 | .footnote-definition + :not(.footnote-definition) {
144 | margin-top: 2em;
145 | }
146 | .footnote-definition {
147 | font-size: 0.9em;
148 | margin: 0.5em 0;
149 | }
150 | .footnote-definition p {
151 | display: inline;
152 | }
153 |
154 | .tooltiptext {
155 | position: absolute;
156 | visibility: hidden;
157 | color: #fff;
158 | background-color: #333;
159 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */
160 | left: -8px; /* Half of the width of the icon */
161 | top: -35px;
162 | font-size: 0.8em;
163 | text-align: center;
164 | border-radius: 6px;
165 | padding: 5px 8px;
166 | margin: 5px;
167 | z-index: 1000;
168 | }
169 | .tooltipped .tooltiptext {
170 | visibility: visible;
171 | }
172 |
173 | .chapter li.part-title {
174 | color: var(--sidebar-fg);
175 | margin: 5px 0px;
176 | font-weight: bold;
177 | }
178 |
179 | .result-no-output {
180 | font-style: italic;
181 | }
182 |
--------------------------------------------------------------------------------
/theme/css/variables.css:
--------------------------------------------------------------------------------
1 |
2 | /* Globals */
3 |
4 | :root {
5 | --sidebar-width: 300px;
6 | --page-padding: 15px;
7 | --content-max-width: 750px;
8 | --menu-bar-height: 50px;
9 | }
10 |
11 | /* Themes */
12 |
13 | .ayu {
14 | --bg: hsl(210, 25%, 8%);
15 | --fg: #c5c5c5;
16 |
17 | --sidebar-bg: #14191f;
18 | --sidebar-fg: #c8c9db;
19 | --sidebar-non-existant: #5c6773;
20 | --sidebar-active: #ffb454;
21 | --sidebar-spacer: #2d334f;
22 |
23 | --scrollbar: var(--sidebar-fg);
24 |
25 | --icons: #737480;
26 | --icons-hover: #b7b9cc;
27 |
28 | --links: #0096cf;
29 |
30 | --inline-code-color: #ffb454;
31 |
32 | --theme-popup-bg: #14191f;
33 | --theme-popup-border: #5c6773;
34 | --theme-hover: #191f26;
35 |
36 | --quote-bg: hsl(226, 15%, 17%);
37 | --quote-border: hsl(226, 15%, 22%);
38 |
39 | --table-border-color: hsl(210, 25%, 13%);
40 | --table-header-bg: hsl(210, 25%, 28%);
41 | --table-alternate-bg: hsl(210, 25%, 11%);
42 |
43 | --searchbar-border-color: #848484;
44 | --searchbar-bg: #424242;
45 | --searchbar-fg: #fff;
46 | --searchbar-shadow-color: #d4c89f;
47 | --searchresults-header-fg: #666;
48 | --searchresults-border-color: #888;
49 | --searchresults-li-bg: #252932;
50 | --search-mark-bg: #e3b171;
51 | }
52 |
53 | .coal {
54 | --bg: hsl(200, 7%, 8%);
55 | --fg: #98a3ad;
56 |
57 | --sidebar-bg: #292c2f;
58 | --sidebar-fg: #a1adb8;
59 | --sidebar-non-existant: #505254;
60 | --sidebar-active: #3473ad;
61 | --sidebar-spacer: #393939;
62 |
63 | --scrollbar: var(--sidebar-fg);
64 |
65 | --icons: #43484d;
66 | --icons-hover: #b3c0cc;
67 |
68 | --links: #2b79a2;
69 |
70 | --inline-code-color: #c5c8c6;
71 |
72 | --theme-popup-bg: #141617;
73 | --theme-popup-border: #43484d;
74 | --theme-hover: #1f2124;
75 |
76 | --quote-bg: hsl(234, 21%, 18%);
77 | --quote-border: hsl(234, 21%, 23%);
78 |
79 | --table-border-color: hsl(200, 7%, 13%);
80 | --table-header-bg: hsl(200, 7%, 28%);
81 | --table-alternate-bg: hsl(200, 7%, 11%);
82 |
83 | --searchbar-border-color: #aaa;
84 | --searchbar-bg: #b7b7b7;
85 | --searchbar-fg: #000;
86 | --searchbar-shadow-color: #aaa;
87 | --searchresults-header-fg: #666;
88 | --searchresults-border-color: #98a3ad;
89 | --searchresults-li-bg: #2b2b2f;
90 | --search-mark-bg: #355c7d;
91 | }
92 |
93 | .light {
94 | --bg: hsl(0, 0%, 100%);
95 | --fg: hsl(0, 0%, 0%);
96 |
97 | --sidebar-bg: #fafafa;
98 | --sidebar-fg: hsl(0, 0%, 0%);
99 | --sidebar-non-existant: #aaaaaa;
100 | --sidebar-active: #1f1fff;
101 | --sidebar-spacer: #f4f4f4;
102 |
103 | --scrollbar: #8F8F8F;
104 |
105 | --icons: #747474;
106 | --icons-hover: #000000;
107 |
108 | --links: #20609f;
109 |
110 | --inline-code-color: #301900;
111 |
112 | --theme-popup-bg: #fafafa;
113 | --theme-popup-border: #cccccc;
114 | --theme-hover: #e6e6e6;
115 |
116 | --quote-bg: hsl(197, 37%, 96%);
117 | --quote-border: hsl(197, 37%, 91%);
118 |
119 | --table-border-color: hsl(0, 0%, 95%);
120 | --table-header-bg: hsl(0, 0%, 80%);
121 | --table-alternate-bg: hsl(0, 0%, 97%);
122 |
123 | --searchbar-border-color: #aaa;
124 | --searchbar-bg: #fafafa;
125 | --searchbar-fg: #000;
126 | --searchbar-shadow-color: #aaa;
127 | --searchresults-header-fg: #666;
128 | --searchresults-border-color: #888;
129 | --searchresults-li-bg: #e4f2fe;
130 | --search-mark-bg: #a2cff5;
131 | }
132 |
133 | .navy {
134 | --bg: hsl(226, 23%, 11%);
135 | --fg: #bcbdd0;
136 |
137 | --sidebar-bg: #282d3f;
138 | --sidebar-fg: #c8c9db;
139 | --sidebar-non-existant: #505274;
140 | --sidebar-active: #2b79a2;
141 | --sidebar-spacer: #2d334f;
142 |
143 | --scrollbar: var(--sidebar-fg);
144 |
145 | --icons: #737480;
146 | --icons-hover: #b7b9cc;
147 |
148 | --links: #2b79a2;
149 |
150 | --inline-code-color: #c5c8c6;
151 |
152 | --theme-popup-bg: #161923;
153 | --theme-popup-border: #737480;
154 | --theme-hover: #282e40;
155 |
156 | --quote-bg: hsl(226, 15%, 17%);
157 | --quote-border: hsl(226, 15%, 22%);
158 |
159 | --table-border-color: hsl(226, 23%, 16%);
160 | --table-header-bg: hsl(226, 23%, 31%);
161 | --table-alternate-bg: hsl(226, 23%, 14%);
162 |
163 | --searchbar-border-color: #aaa;
164 | --searchbar-bg: #aeaec6;
165 | --searchbar-fg: #000;
166 | --searchbar-shadow-color: #aaa;
167 | --searchresults-header-fg: #5f5f71;
168 | --searchresults-border-color: #5c5c68;
169 | --searchresults-li-bg: #242430;
170 | --search-mark-bg: #a2cff5;
171 | }
172 |
173 | .rust {
174 | --bg: hsl(60, 9%, 87%);
175 | --fg: #262625;
176 |
177 | --sidebar-bg: #3b2e2a;
178 | --sidebar-fg: #c8c9db;
179 | --sidebar-non-existant: #505254;
180 | --sidebar-active: #e69f67;
181 | --sidebar-spacer: #45373a;
182 |
183 | --scrollbar: var(--sidebar-fg);
184 |
185 | --icons: #737480;
186 | --icons-hover: #262625;
187 |
188 | --links: #2b79a2;
189 |
190 | --inline-code-color: #6e6b5e;
191 |
192 | --theme-popup-bg: #e1e1db;
193 | --theme-popup-border: #b38f6b;
194 | --theme-hover: #99908a;
195 |
196 | --quote-bg: hsl(60, 5%, 75%);
197 | --quote-border: hsl(60, 5%, 70%);
198 |
199 | --table-border-color: hsl(60, 9%, 82%);
200 | --table-header-bg: #b3a497;
201 | --table-alternate-bg: hsl(60, 9%, 84%);
202 |
203 | --searchbar-border-color: #aaa;
204 | --searchbar-bg: #fafafa;
205 | --searchbar-fg: #000;
206 | --searchbar-shadow-color: #aaa;
207 | --searchresults-header-fg: #666;
208 | --searchresults-border-color: #888;
209 | --searchresults-li-bg: #dec2a2;
210 | --search-mark-bg: #e69f67;
211 | }
212 |
213 | @media (prefers-color-scheme: dark) {
214 | .light.no-js {
215 | --bg: hsl(200, 7%, 8%);
216 | --fg: #98a3ad;
217 |
218 | --sidebar-bg: #292c2f;
219 | --sidebar-fg: #a1adb8;
220 | --sidebar-non-existant: #505254;
221 | --sidebar-active: #3473ad;
222 | --sidebar-spacer: #393939;
223 |
224 | --scrollbar: var(--sidebar-fg);
225 |
226 | --icons: #43484d;
227 | --icons-hover: #b3c0cc;
228 |
229 | --links: #2b79a2;
230 |
231 | --inline-code-color: #c5c8c6;
232 |
233 | --theme-popup-bg: #141617;
234 | --theme-popup-border: #43484d;
235 | --theme-hover: #1f2124;
236 |
237 | --quote-bg: hsl(234, 21%, 18%);
238 | --quote-border: hsl(234, 21%, 23%);
239 |
240 | --table-border-color: hsl(200, 7%, 13%);
241 | --table-header-bg: hsl(200, 7%, 28%);
242 | --table-alternate-bg: hsl(200, 7%, 11%);
243 |
244 | --searchbar-border-color: #aaa;
245 | --searchbar-bg: #b7b7b7;
246 | --searchbar-fg: #000;
247 | --searchbar-shadow-color: #aaa;
248 | --searchresults-header-fg: #666;
249 | --searchresults-border-color: #98a3ad;
250 | --searchresults-li-bg: #2b2b2f;
251 | --search-mark-bg: #355c7d;
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/theme/css/chrome.css:
--------------------------------------------------------------------------------
1 | /* CSS for UI elements (a.k.a. chrome) */
2 |
3 | @import 'variables.css';
4 |
5 | ::-webkit-scrollbar {
6 | background: var(--bg);
7 | }
8 | ::-webkit-scrollbar-thumb {
9 | background: var(--scrollbar);
10 | }
11 | html {
12 | scrollbar-color: var(--scrollbar) var(--bg);
13 | }
14 | #searchresults a,
15 | .content a:link,
16 | a:visited,
17 | a > .hljs {
18 | color: var(--links);
19 | }
20 |
21 | /* Menu Bar */
22 |
23 | #menu-bar,
24 | #menu-bar-hover-placeholder {
25 | z-index: 101;
26 | margin: auto calc(0px - var(--page-padding));
27 | }
28 | #menu-bar {
29 | position: relative;
30 | display: flex;
31 | flex-wrap: wrap;
32 | background-color: var(--bg);
33 | border-bottom-color: var(--bg);
34 | border-bottom-width: 1px;
35 | border-bottom-style: solid;
36 | }
37 | #menu-bar.sticky,
38 | .js #menu-bar-hover-placeholder:hover + #menu-bar,
39 | .js #menu-bar:hover,
40 | .js.sidebar-visible #menu-bar {
41 | position: -webkit-sticky;
42 | position: sticky;
43 | top: 0 !important;
44 | }
45 | #menu-bar-hover-placeholder {
46 | position: sticky;
47 | position: -webkit-sticky;
48 | top: 0;
49 | height: var(--menu-bar-height);
50 | }
51 | #menu-bar.bordered {
52 | border-bottom-color: var(--table-border-color);
53 | }
54 | #menu-bar i, #menu-bar .icon-button {
55 | position: relative;
56 | padding: 0 8px;
57 | z-index: 10;
58 | line-height: var(--menu-bar-height);
59 | cursor: pointer;
60 | transition: color 0.5s;
61 | }
62 | @media only screen and (max-width: 420px) {
63 | #menu-bar i, #menu-bar .icon-button {
64 | padding: 0 5px;
65 | }
66 | }
67 |
68 | .icon-button {
69 | border: none;
70 | background: none;
71 | padding: 0;
72 | color: inherit;
73 | }
74 | .icon-button i {
75 | margin: 0;
76 | }
77 |
78 | .right-buttons {
79 | margin: 0 15px;
80 | }
81 | .right-buttons a {
82 | text-decoration: none;
83 | }
84 |
85 | .left-buttons {
86 | display: flex;
87 | margin: 0 5px;
88 | }
89 | .no-js .left-buttons {
90 | display: none;
91 | }
92 |
93 | .menu-title {
94 | display: inline-block;
95 | font-weight: 200;
96 | font-size: 2.4rem;
97 | line-height: var(--menu-bar-height);
98 | text-align: center;
99 | margin: 0;
100 | flex: 1;
101 | white-space: nowrap;
102 | overflow: hidden;
103 | text-overflow: ellipsis;
104 | }
105 | .js .menu-title {
106 | cursor: pointer;
107 | }
108 |
109 | .menu-bar,
110 | .menu-bar:visited,
111 | .nav-chapters,
112 | .nav-chapters:visited,
113 | .mobile-nav-chapters,
114 | .mobile-nav-chapters:visited,
115 | .menu-bar .icon-button,
116 | .menu-bar a i {
117 | color: var(--icons);
118 | }
119 |
120 | .menu-bar i:hover,
121 | .menu-bar .icon-button:hover,
122 | .nav-chapters:hover,
123 | .mobile-nav-chapters i:hover {
124 | color: var(--icons-hover);
125 | }
126 |
127 | /* Nav Icons */
128 |
129 | .nav-chapters {
130 | font-size: 2.5em;
131 | text-align: center;
132 | text-decoration: none;
133 |
134 | position: fixed;
135 | top: 0;
136 | bottom: 0;
137 | margin: 0;
138 | max-width: 150px;
139 | min-width: 90px;
140 |
141 | display: flex;
142 | justify-content: center;
143 | align-content: center;
144 | flex-direction: column;
145 |
146 | transition: color 0.5s, background-color 0.5s;
147 | }
148 |
149 | .nav-chapters:hover {
150 | text-decoration: none;
151 | background-color: var(--theme-hover);
152 | transition: background-color 0.15s, color 0.15s;
153 | }
154 |
155 | .nav-wrapper {
156 | margin-top: 50px;
157 | display: none;
158 | }
159 |
160 | .mobile-nav-chapters {
161 | font-size: 2.5em;
162 | text-align: center;
163 | text-decoration: none;
164 | width: 90px;
165 | border-radius: 5px;
166 | background-color: var(--sidebar-bg);
167 | }
168 |
169 | .previous {
170 | float: left;
171 | }
172 |
173 | .next {
174 | float: right;
175 | right: var(--page-padding);
176 | }
177 |
178 | @media only screen and (max-width: 1080px) {
179 | .nav-wide-wrapper { display: none; }
180 | .nav-wrapper { display: block; }
181 | }
182 |
183 | @media only screen and (max-width: 1380px) {
184 | .sidebar-visible .nav-wide-wrapper { display: none; }
185 | .sidebar-visible .nav-wrapper { display: block; }
186 | }
187 |
188 | /* Inline code */
189 |
190 | :not(pre) > .hljs {
191 | display: inline;
192 | padding: 0.1em 0.3em;
193 | border-radius: 3px;
194 | }
195 |
196 | :not(pre):not(a) > .hljs {
197 | color: var(--inline-code-color);
198 | overflow-x: initial;
199 | }
200 |
201 | a:hover > .hljs {
202 | text-decoration: underline;
203 | }
204 |
205 | pre {
206 | position: relative;
207 | }
208 | pre > .buttons {
209 | position: absolute;
210 | z-index: 100;
211 | right: 5px;
212 | top: 5px;
213 |
214 | color: var(--sidebar-fg);
215 | cursor: pointer;
216 | }
217 | pre > .buttons :hover {
218 | color: var(--sidebar-active);
219 | }
220 | pre > .buttons i {
221 | margin-left: 8px;
222 | }
223 | pre > .buttons button {
224 | color: inherit;
225 | background: transparent;
226 | border: none;
227 | cursor: inherit;
228 | }
229 | pre > .result {
230 | margin-top: 10px;
231 | }
232 |
233 | /* Search */
234 |
235 | #searchresults a {
236 | text-decoration: none;
237 | }
238 |
239 | mark {
240 | border-radius: 2px;
241 | padding: 0 3px 1px 3px;
242 | margin: 0 -3px -1px -3px;
243 | background-color: var(--search-mark-bg);
244 | transition: background-color 300ms linear;
245 | cursor: pointer;
246 | }
247 |
248 | mark.fade-out {
249 | background-color: rgba(0,0,0,0) !important;
250 | cursor: auto;
251 | }
252 |
253 | .searchbar-outer {
254 | margin-left: auto;
255 | margin-right: auto;
256 | max-width: var(--content-max-width);
257 | }
258 |
259 | #searchbar {
260 | width: 100%;
261 | margin: 5px auto 0px auto;
262 | padding: 10px 16px;
263 | transition: box-shadow 300ms ease-in-out;
264 | border: 1px solid var(--searchbar-border-color);
265 | border-radius: 3px;
266 | background-color: var(--searchbar-bg);
267 | color: var(--searchbar-fg);
268 | }
269 | #searchbar:focus,
270 | #searchbar.active {
271 | box-shadow: 0 0 3px var(--searchbar-shadow-color);
272 | }
273 |
274 | .searchresults-header {
275 | font-weight: bold;
276 | font-size: 1em;
277 | padding: 18px 0 0 5px;
278 | color: var(--searchresults-header-fg);
279 | }
280 |
281 | .searchresults-outer {
282 | margin-left: auto;
283 | margin-right: auto;
284 | max-width: var(--content-max-width);
285 | border-bottom: 1px dashed var(--searchresults-border-color);
286 | }
287 |
288 | ul#searchresults {
289 | list-style: none;
290 | padding-left: 20px;
291 | }
292 | ul#searchresults li {
293 | margin: 10px 0px;
294 | padding: 2px;
295 | border-radius: 2px;
296 | }
297 | ul#searchresults li.focus {
298 | background-color: var(--searchresults-li-bg);
299 | }
300 | ul#searchresults span.teaser {
301 | display: block;
302 | clear: both;
303 | margin: 5px 0 0 20px;
304 | font-size: 0.8em;
305 | }
306 | ul#searchresults span.teaser em {
307 | font-weight: bold;
308 | font-style: normal;
309 | }
310 |
311 | /* Sidebar */
312 |
313 | .sidebar {
314 | position: fixed;
315 | left: 0;
316 | top: 0;
317 | bottom: 0;
318 | width: var(--sidebar-width);
319 | font-size: 0.875em;
320 | box-sizing: border-box;
321 | -webkit-overflow-scrolling: touch;
322 | overscroll-behavior-y: contain;
323 | background-color: var(--sidebar-bg);
324 | color: var(--sidebar-fg);
325 | }
326 | .sidebar-resizing {
327 | -moz-user-select: none;
328 | -webkit-user-select: none;
329 | -ms-user-select: none;
330 | user-select: none;
331 | }
332 | .js:not(.sidebar-resizing) .sidebar {
333 | transition: transform 0.3s; /* Animation: slide away */
334 | }
335 | .sidebar code {
336 | line-height: 2em;
337 | }
338 | .sidebar .sidebar-scrollbox {
339 | overflow-y: auto;
340 | position: absolute;
341 | top: 0;
342 | bottom: 0;
343 | left: 0;
344 | right: 0;
345 | padding: 10px 10px;
346 | }
347 | .sidebar .sidebar-resize-handle {
348 | position: absolute;
349 | cursor: col-resize;
350 | width: 0;
351 | right: 0;
352 | top: 0;
353 | bottom: 0;
354 | }
355 | .js .sidebar .sidebar-resize-handle {
356 | cursor: col-resize;
357 | width: 5px;
358 | }
359 | .sidebar-hidden .sidebar {
360 | transform: translateX(calc(0px - var(--sidebar-width)));
361 | }
362 | .sidebar::-webkit-scrollbar {
363 | background: var(--sidebar-bg);
364 | }
365 | .sidebar::-webkit-scrollbar-thumb {
366 | background: var(--scrollbar);
367 | }
368 |
369 | .sidebar-visible .page-wrapper {
370 | transform: translateX(var(--sidebar-width));
371 | }
372 | @media only screen and (min-width: 620px) {
373 | .sidebar-visible .page-wrapper {
374 | transform: none;
375 | margin-left: var(--sidebar-width);
376 | }
377 | }
378 |
379 | .chapter {
380 | list-style: none outside none;
381 | padding-left: 0;
382 | line-height: 2.2em;
383 | }
384 |
385 | .chapter ol {
386 | width: 100%;
387 | }
388 |
389 | .chapter li {
390 | display: flex;
391 | color: var(--sidebar-non-existant);
392 | }
393 | .chapter li a {
394 | display: block;
395 | padding: 0;
396 | text-decoration: none;
397 | color: var(--sidebar-fg);
398 | }
399 |
400 | .chapter li a:hover {
401 | color: var(--sidebar-active);
402 | }
403 |
404 | .chapter li a.active {
405 | color: var(--sidebar-active);
406 | }
407 |
408 | .chapter li > a.toggle {
409 | cursor: pointer;
410 | display: block;
411 | margin-left: auto;
412 | padding: 0 10px;
413 | user-select: none;
414 | opacity: 0.68;
415 | }
416 |
417 | .chapter li > a.toggle div {
418 | transition: transform 0.5s;
419 | }
420 |
421 | /* collapse the section */
422 | .chapter li:not(.expanded) + li > ol {
423 | display: none;
424 | }
425 |
426 | .chapter li.chapter-item {
427 | line-height: 1.5em;
428 | margin-top: 0.6em;
429 | }
430 |
431 | .chapter li.expanded > a.toggle div {
432 | transform: rotate(90deg);
433 | }
434 |
435 | .spacer {
436 | width: 100%;
437 | height: 3px;
438 | margin: 5px 0px;
439 | }
440 | .chapter .spacer {
441 | background-color: var(--sidebar-spacer);
442 | }
443 |
444 | @media (-moz-touch-enabled: 1), (pointer: coarse) {
445 | .chapter li a { padding: 5px 0; }
446 | .spacer { margin: 10px 0; }
447 | }
448 |
449 | .section {
450 | list-style: none outside none;
451 | padding-left: 20px;
452 | line-height: 1.9em;
453 | }
454 |
455 | /* Theme Menu Popup */
456 |
457 | .theme-popup {
458 | position: absolute;
459 | left: 10px;
460 | top: var(--menu-bar-height);
461 | z-index: 1000;
462 | border-radius: 4px;
463 | font-size: 0.7em;
464 | color: var(--fg);
465 | background: var(--theme-popup-bg);
466 | border: 1px solid var(--theme-popup-border);
467 | margin: 0;
468 | padding: 0;
469 | list-style: none;
470 | display: none;
471 | }
472 | .theme-popup .default {
473 | color: var(--icons);
474 | }
475 | .theme-popup .theme {
476 | width: 100%;
477 | border: 0;
478 | margin: 0;
479 | padding: 2px 10px;
480 | line-height: 25px;
481 | white-space: nowrap;
482 | text-align: left;
483 | cursor: pointer;
484 | color: inherit;
485 | background: inherit;
486 | font-size: inherit;
487 | }
488 | .theme-popup .theme:hover {
489 | background-color: var(--theme-hover);
490 | }
491 | .theme-popup .theme:hover:first-child,
492 | .theme-popup .theme:hover:last-child {
493 | border-top-left-radius: inherit;
494 | border-top-right-radius: inherit;
495 | }
496 |
--------------------------------------------------------------------------------
/theme/index.hbs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ title }}
7 | {{#if is_print }}
8 |
9 | {{/if}}
10 | {{#if base_url}}
11 |
12 | {{/if}}
13 |
14 |
15 |
16 | {{> head}}
17 |
18 |
19 |
20 |
21 |
22 |
23 | {{#if favicon_svg}}
24 |
25 | {{/if}}
26 | {{#if favicon_png}}
27 |
28 | {{/if}}
29 |
30 |
31 |
32 | {{#if print_enable}}
33 |
34 | {{/if}}
35 |
36 |
37 |
38 | {{#if copy_fonts}}
39 |
40 | {{/if}}
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | {{#each additional_css}}
49 |
50 | {{/each}}
51 |
52 | {{#if mathjax_support}}
53 |
54 |
55 | {{/if}}
56 |
57 |
58 |
59 |
63 |
64 |
65 |
79 |
80 |
81 |
91 |
92 |
93 |
103 |
104 |
112 |
113 |
114 |
115 |
116 | {{> header}}
117 |
118 |
161 |
162 | {{#if search_enabled}}
163 |
173 | {{/if}}
174 |
175 |
176 |
183 |
184 |
185 |
186 | {{{ content }}}
187 |
188 |
189 |
205 |
206 |
207 |
208 |
221 |
222 |
223 |
224 | {{#if livereload}}
225 |
226 |
239 | {{/if}}
240 |
241 | {{#if google_analytics}}
242 |
243 |
258 | {{/if}}
259 |
260 | {{#if playground_line_numbers}}
261 |
264 | {{/if}}
265 |
266 | {{#if playground_copyable}}
267 |
270 | {{/if}}
271 |
272 | {{#if playground_js}}
273 |
274 |
275 |
276 |
277 |
278 | {{/if}}
279 |
280 | {{#if search_js}}
281 |
282 |
283 |
284 | {{/if}}
285 |
286 |
287 |
288 |
289 |
290 |
291 | {{#each additional_js}}
292 |
293 | {{/each}}
294 |
295 | {{#if is_print}}
296 | {{#if mathjax_support}}
297 |
304 | {{else}}
305 |
310 | {{/if}}
311 | {{/if}}
312 |
313 |
314 |
315 |
--------------------------------------------------------------------------------
/theme/book.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Fix back button cache problem
4 | window.onunload = function () { };
5 |
6 | // Global variable, shared between modules
7 | function playground_text(playground) {
8 | let code_block = playground.querySelector("code");
9 |
10 | if (window.ace && code_block.classList.contains("editable")) {
11 | let editor = window.ace.edit(code_block);
12 | return editor.getValue();
13 | } else {
14 | return code_block.textContent;
15 | }
16 | }
17 |
18 | (function codeSnippets() {
19 | function fetch_with_timeout(url, options, timeout = 6000) {
20 | return Promise.race([
21 | fetch(url, options),
22 | new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
23 | ]);
24 | }
25 |
26 | var playgrounds = Array.from(document.querySelectorAll(".playground"));
27 | if (playgrounds.length > 0) {
28 | fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
29 | headers: {
30 | 'Content-Type': "application/json",
31 | },
32 | method: 'POST',
33 | mode: 'cors',
34 | })
35 | .then(response => response.json())
36 | .then(response => {
37 | // get list of crates available in the rust playground
38 | let playground_crates = response.crates.map(item => item["id"]);
39 | playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
40 | });
41 | }
42 |
43 | function handle_crate_list_update(playground_block, playground_crates) {
44 | // update the play buttons after receiving the response
45 | update_play_button(playground_block, playground_crates);
46 |
47 | // and install on change listener to dynamically update ACE editors
48 | if (window.ace) {
49 | let code_block = playground_block.querySelector("code");
50 | if (code_block.classList.contains("editable")) {
51 | let editor = window.ace.edit(code_block);
52 | editor.addEventListener("change", function (e) {
53 | update_play_button(playground_block, playground_crates);
54 | });
55 | // add Ctrl-Enter command to execute rust code
56 | editor.commands.addCommand({
57 | name: "run",
58 | bindKey: {
59 | win: "Ctrl-Enter",
60 | mac: "Ctrl-Enter"
61 | },
62 | exec: _editor => run_rust_code(playground_block)
63 | });
64 | }
65 | }
66 | }
67 |
68 | // updates the visibility of play button based on `no_run` class and
69 | // used crates vs ones available on http://play.rust-lang.org
70 | function update_play_button(pre_block, playground_crates) {
71 | var play_button = pre_block.querySelector(".play-button");
72 |
73 | // skip if code is `no_run`
74 | if (pre_block.querySelector('code').classList.contains("no_run")) {
75 | play_button.classList.add("hidden");
76 | return;
77 | }
78 |
79 | // get list of `extern crate`'s from snippet
80 | var txt = playground_text(pre_block);
81 | var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
82 | var snippet_crates = [];
83 | var item;
84 | while (item = re.exec(txt)) {
85 | snippet_crates.push(item[1]);
86 | }
87 |
88 | // check if all used crates are available on play.rust-lang.org
89 | var all_available = snippet_crates.every(function (elem) {
90 | return playground_crates.indexOf(elem) > -1;
91 | });
92 |
93 | if (all_available) {
94 | play_button.classList.remove("hidden");
95 | } else {
96 | play_button.classList.add("hidden");
97 | }
98 | }
99 |
100 | function run_rust_code(code_block) {
101 | var result_block = code_block.querySelector(".result");
102 | if (!result_block) {
103 | result_block = document.createElement('code');
104 | result_block.className = 'result hljs language-bash';
105 |
106 | code_block.append(result_block);
107 | }
108 |
109 | let text = playground_text(code_block);
110 | let classes = code_block.querySelector('code').classList;
111 | let edition = "2015";
112 | if(classes.contains("edition2018")) {
113 | edition = "2018";
114 | } else if(classes.contains("edition2021")) {
115 | edition = "2021";
116 | }
117 | var params = {
118 | version: "stable",
119 | optimize: "0",
120 | code: text,
121 | edition: edition
122 | };
123 |
124 | if (text.indexOf("#![feature") !== -1) {
125 | params.version = "nightly";
126 | }
127 |
128 | result_block.innerText = "Running...";
129 |
130 | fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
131 | headers: {
132 | 'Content-Type': "application/json",
133 | },
134 | method: 'POST',
135 | mode: 'cors',
136 | body: JSON.stringify(params)
137 | })
138 | .then(response => response.json())
139 | .then(response => {
140 | if (response.result.trim() === '') {
141 | result_block.innerText = "No output";
142 | result_block.classList.add("result-no-output");
143 | } else {
144 | result_block.innerText = response.result;
145 | result_block.classList.remove("result-no-output");
146 | }
147 | })
148 | .catch(error => result_block.innerText = "Playground Communication: " + error.message);
149 | }
150 |
151 | // Syntax highlighting Configuration
152 | hljs.configure({
153 | tabReplace: ' ', // 4 spaces
154 | languages: [], // Languages used for auto-detection
155 | });
156 |
157 | let code_nodes = Array
158 | .from(document.querySelectorAll('code'))
159 | // Don't highlight `inline code` blocks in headers.
160 | .filter(function (node) {return !node.parentElement.classList.contains("header"); });
161 |
162 | if (window.ace) {
163 | // language-rust class needs to be removed for editable
164 | // blocks or highlightjs will capture events
165 | code_nodes
166 | .filter(function (node) {return node.classList.contains("editable"); })
167 | .forEach(function (block) { block.classList.remove('language-rust'); });
168 |
169 | Array
170 | code_nodes
171 | .filter(function (node) {return !node.classList.contains("editable"); })
172 | .forEach(function (block) { hljs.highlightBlock(block); });
173 | } else {
174 | code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
175 | }
176 |
177 | // Adding the hljs class gives code blocks the color css
178 | // even if highlighting doesn't apply
179 | code_nodes.forEach(function (block) { block.classList.add('hljs'); });
180 |
181 | Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
182 |
183 | var lines = Array.from(block.querySelectorAll('.boring'));
184 | // If no lines were hidden, return
185 | if (!lines.length) { return; }
186 | block.classList.add("hide-boring");
187 |
188 | var buttons = document.createElement('div');
189 | buttons.className = 'buttons';
190 | buttons.innerHTML = "";
191 |
192 | // add expand button
193 | var pre_block = block.parentNode;
194 | pre_block.insertBefore(buttons, pre_block.firstChild);
195 |
196 | pre_block.querySelector('.buttons').addEventListener('click', function (e) {
197 | if (e.target.classList.contains('fa-eye')) {
198 | e.target.classList.remove('fa-eye');
199 | e.target.classList.add('fa-eye-slash');
200 | e.target.title = 'Hide lines';
201 | e.target.setAttribute('aria-label', e.target.title);
202 |
203 | block.classList.remove('hide-boring');
204 | } else if (e.target.classList.contains('fa-eye-slash')) {
205 | e.target.classList.remove('fa-eye-slash');
206 | e.target.classList.add('fa-eye');
207 | e.target.title = 'Show hidden lines';
208 | e.target.setAttribute('aria-label', e.target.title);
209 |
210 | block.classList.add('hide-boring');
211 | }
212 | });
213 | });
214 |
215 | if (window.playground_copyable) {
216 | Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
217 | var pre_block = block.parentNode;
218 | if (!pre_block.classList.contains('playground')) {
219 | var buttons = pre_block.querySelector(".buttons");
220 | if (!buttons) {
221 | buttons = document.createElement('div');
222 | buttons.className = 'buttons';
223 | pre_block.insertBefore(buttons, pre_block.firstChild);
224 | }
225 |
226 | var clipButton = document.createElement('button');
227 | clipButton.className = 'fa fa-copy clip-button';
228 | clipButton.title = 'Copy to clipboard';
229 | clipButton.setAttribute('aria-label', clipButton.title);
230 | clipButton.innerHTML = '';
231 |
232 | buttons.insertBefore(clipButton, buttons.firstChild);
233 | }
234 | });
235 | }
236 |
237 | // Process playground code blocks
238 | Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) {
239 | // Add play button
240 | var buttons = pre_block.querySelector(".buttons");
241 | if (!buttons) {
242 | buttons = document.createElement('div');
243 | buttons.className = 'buttons';
244 | pre_block.insertBefore(buttons, pre_block.firstChild);
245 | }
246 |
247 | var runCodeButton = document.createElement('button');
248 | runCodeButton.className = 'fa fa-play play-button';
249 | runCodeButton.hidden = true;
250 | runCodeButton.title = 'Run this code';
251 | runCodeButton.setAttribute('aria-label', runCodeButton.title);
252 |
253 | buttons.insertBefore(runCodeButton, buttons.firstChild);
254 | runCodeButton.addEventListener('click', function (e) {
255 | run_rust_code(pre_block);
256 | });
257 |
258 | if (window.playground_copyable) {
259 | var copyCodeClipboardButton = document.createElement('button');
260 | copyCodeClipboardButton.className = 'fa fa-copy clip-button';
261 | copyCodeClipboardButton.innerHTML = '';
262 | copyCodeClipboardButton.title = 'Copy to clipboard';
263 | copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
264 |
265 | buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
266 | }
267 |
268 | let code_block = pre_block.querySelector("code");
269 | if (window.ace && code_block.classList.contains("editable")) {
270 | var undoChangesButton = document.createElement('button');
271 | undoChangesButton.className = 'fa fa-history reset-button';
272 | undoChangesButton.title = 'Undo changes';
273 | undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
274 |
275 | buttons.insertBefore(undoChangesButton, buttons.firstChild);
276 |
277 | undoChangesButton.addEventListener('click', function () {
278 | let editor = window.ace.edit(code_block);
279 | editor.setValue(editor.originalCode);
280 | editor.clearSelection();
281 | });
282 | }
283 | });
284 | })();
285 |
286 | (function themes() {
287 | var html = document.querySelector('html');
288 | var themeToggleButton = document.getElementById('theme-toggle');
289 | var themePopup = document.getElementById('theme-list');
290 | var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
291 | var stylesheets = {
292 | ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
293 | tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
294 | highlight: document.querySelector("[href$='highlight.css']"),
295 | };
296 |
297 | function showThemes() {
298 | themePopup.style.display = 'block';
299 | themeToggleButton.setAttribute('aria-expanded', true);
300 | themePopup.querySelector("button#" + get_theme()).focus();
301 | }
302 |
303 | function hideThemes() {
304 | themePopup.style.display = 'none';
305 | themeToggleButton.setAttribute('aria-expanded', false);
306 | themeToggleButton.focus();
307 | }
308 |
309 | function get_theme() {
310 | var theme;
311 | try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
312 | if (theme === null || theme === undefined) {
313 | return default_theme;
314 | } else {
315 | return theme;
316 | }
317 | }
318 |
319 | function set_theme(theme, store = true) {
320 | let ace_theme;
321 |
322 | if (theme == 'coal' || theme == 'navy') {
323 | stylesheets.ayuHighlight.disabled = true;
324 | stylesheets.tomorrowNight.disabled = false;
325 | stylesheets.highlight.disabled = true;
326 |
327 | ace_theme = "ace/theme/tomorrow_night";
328 | } else if (theme == 'ayu') {
329 | stylesheets.ayuHighlight.disabled = false;
330 | stylesheets.tomorrowNight.disabled = true;
331 | stylesheets.highlight.disabled = true;
332 | ace_theme = "ace/theme/tomorrow_night";
333 | } else {
334 | stylesheets.ayuHighlight.disabled = true;
335 | stylesheets.tomorrowNight.disabled = true;
336 | stylesheets.highlight.disabled = false;
337 | ace_theme = "ace/theme/dawn";
338 | }
339 |
340 | setTimeout(function () {
341 | themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
342 | }, 1);
343 |
344 | if (window.ace && window.editors) {
345 | window.editors.forEach(function (editor) {
346 | editor.setTheme(ace_theme);
347 | });
348 | }
349 |
350 | var previousTheme = get_theme();
351 |
352 | if (store) {
353 | try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
354 | }
355 |
356 | html.classList.remove(previousTheme);
357 | html.classList.add(theme);
358 | }
359 |
360 | // Set theme
361 | var theme = get_theme();
362 |
363 | set_theme(theme, false);
364 |
365 | themeToggleButton.addEventListener('click', function () {
366 | if (themePopup.style.display === 'block') {
367 | hideThemes();
368 | } else {
369 | showThemes();
370 | }
371 | });
372 |
373 | themePopup.addEventListener('click', function (e) {
374 | var theme;
375 | if (e.target.className === "theme") {
376 | theme = e.target.id;
377 | } else if (e.target.parentElement.className === "theme") {
378 | theme = e.target.parentElement.id;
379 | } else {
380 | return;
381 | }
382 | set_theme(theme);
383 | });
384 |
385 | themePopup.addEventListener('focusout', function(e) {
386 | // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
387 | if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
388 | hideThemes();
389 | }
390 | });
391 |
392 | // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
393 | document.addEventListener('click', function(e) {
394 | if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
395 | hideThemes();
396 | }
397 | });
398 |
399 | document.addEventListener('keydown', function (e) {
400 | if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
401 | if (!themePopup.contains(e.target)) { return; }
402 |
403 | switch (e.key) {
404 | case 'Escape':
405 | e.preventDefault();
406 | hideThemes();
407 | break;
408 | case 'ArrowUp':
409 | e.preventDefault();
410 | var li = document.activeElement.parentElement;
411 | if (li && li.previousElementSibling) {
412 | li.previousElementSibling.querySelector('button').focus();
413 | }
414 | break;
415 | case 'ArrowDown':
416 | e.preventDefault();
417 | var li = document.activeElement.parentElement;
418 | if (li && li.nextElementSibling) {
419 | li.nextElementSibling.querySelector('button').focus();
420 | }
421 | break;
422 | case 'Home':
423 | e.preventDefault();
424 | themePopup.querySelector('li:first-child button').focus();
425 | break;
426 | case 'End':
427 | e.preventDefault();
428 | themePopup.querySelector('li:last-child button').focus();
429 | break;
430 | }
431 | });
432 | })();
433 |
434 | (function sidebar() {
435 | var html = document.querySelector("html");
436 | var sidebar = document.getElementById("sidebar");
437 | var sidebarLinks = document.querySelectorAll('#sidebar a');
438 | var sidebarToggleButton = document.getElementById("sidebar-toggle");
439 | var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
440 | var firstContact = null;
441 |
442 | function showSidebar() {
443 | html.classList.remove('sidebar-hidden')
444 | html.classList.add('sidebar-visible');
445 | Array.from(sidebarLinks).forEach(function (link) {
446 | link.setAttribute('tabIndex', 0);
447 | });
448 | sidebarToggleButton.setAttribute('aria-expanded', true);
449 | sidebar.setAttribute('aria-hidden', false);
450 | try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
451 | }
452 |
453 |
454 | var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
455 |
456 | function toggleSection(ev) {
457 | ev.currentTarget.parentElement.classList.toggle('expanded');
458 | }
459 |
460 | Array.from(sidebarAnchorToggles).forEach(function (el) {
461 | el.addEventListener('click', toggleSection);
462 | });
463 |
464 | function hideSidebar() {
465 | html.classList.remove('sidebar-visible')
466 | html.classList.add('sidebar-hidden');
467 | Array.from(sidebarLinks).forEach(function (link) {
468 | link.setAttribute('tabIndex', -1);
469 | });
470 | sidebarToggleButton.setAttribute('aria-expanded', false);
471 | sidebar.setAttribute('aria-hidden', true);
472 | try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
473 | }
474 |
475 | // Toggle sidebar
476 | sidebarToggleButton.addEventListener('click', function sidebarToggle() {
477 | if (html.classList.contains("sidebar-hidden")) {
478 | var current_width = parseInt(
479 | document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
480 | if (current_width < 150) {
481 | document.documentElement.style.setProperty('--sidebar-width', '150px');
482 | }
483 | showSidebar();
484 | } else if (html.classList.contains("sidebar-visible")) {
485 | hideSidebar();
486 | } else {
487 | if (getComputedStyle(sidebar)['transform'] === 'none') {
488 | hideSidebar();
489 | } else {
490 | showSidebar();
491 | }
492 | }
493 | });
494 |
495 | sidebarResizeHandle.addEventListener('mousedown', initResize, false);
496 |
497 | function initResize(e) {
498 | window.addEventListener('mousemove', resize, false);
499 | window.addEventListener('mouseup', stopResize, false);
500 | html.classList.add('sidebar-resizing');
501 | }
502 | function resize(e) {
503 | var pos = (e.clientX - sidebar.offsetLeft);
504 | if (pos < 20) {
505 | hideSidebar();
506 | } else {
507 | if (html.classList.contains("sidebar-hidden")) {
508 | showSidebar();
509 | }
510 | pos = Math.min(pos, window.innerWidth - 100);
511 | document.documentElement.style.setProperty('--sidebar-width', pos + 'px');
512 | }
513 | }
514 | //on mouseup remove windows functions mousemove & mouseup
515 | function stopResize(e) {
516 | html.classList.remove('sidebar-resizing');
517 | window.removeEventListener('mousemove', resize, false);
518 | window.removeEventListener('mouseup', stopResize, false);
519 | }
520 |
521 | document.addEventListener('touchstart', function (e) {
522 | firstContact = {
523 | x: e.touches[0].clientX,
524 | time: Date.now()
525 | };
526 | }, { passive: true });
527 |
528 | document.addEventListener('touchmove', function (e) {
529 | if (!firstContact)
530 | return;
531 |
532 | var curX = e.touches[0].clientX;
533 | var xDiff = curX - firstContact.x,
534 | tDiff = Date.now() - firstContact.time;
535 |
536 | if (tDiff < 250 && Math.abs(xDiff) >= 150) {
537 | if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
538 | showSidebar();
539 | else if (xDiff < 0 && curX < 300)
540 | hideSidebar();
541 |
542 | firstContact = null;
543 | }
544 | }, { passive: true });
545 |
546 | // Scroll sidebar to current active section
547 | var activeSection = document.getElementById("sidebar").querySelector(".active");
548 | if (activeSection) {
549 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
550 | activeSection.scrollIntoView({ block: 'center' });
551 | }
552 | })();
553 |
554 | (function chapterNavigation() {
555 | document.addEventListener('keydown', function (e) {
556 | if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
557 | if (window.search && window.search.hasFocus()) { return; }
558 |
559 | switch (e.key) {
560 | case 'ArrowRight':
561 | e.preventDefault();
562 | var nextButton = document.querySelector('.nav-chapters.next');
563 | if (nextButton) {
564 | window.location.href = nextButton.href;
565 | }
566 | break;
567 | case 'ArrowLeft':
568 | e.preventDefault();
569 | var previousButton = document.querySelector('.nav-chapters.previous');
570 | if (previousButton) {
571 | window.location.href = previousButton.href;
572 | }
573 | break;
574 | }
575 | });
576 | })();
577 |
578 | (function clipboard() {
579 | var clipButtons = document.querySelectorAll('.clip-button');
580 |
581 | function hideTooltip(elem) {
582 | elem.firstChild.innerText = "";
583 | elem.className = 'fa fa-copy clip-button';
584 | }
585 |
586 | function showTooltip(elem, msg) {
587 | elem.firstChild.innerText = msg;
588 | elem.className = 'fa fa-copy tooltipped';
589 | }
590 |
591 | var clipboardSnippets = new ClipboardJS('.clip-button', {
592 | text: function (trigger) {
593 | hideTooltip(trigger);
594 | let playground = trigger.closest("pre");
595 | return playground_text(playground);
596 | }
597 | });
598 |
599 | Array.from(clipButtons).forEach(function (clipButton) {
600 | clipButton.addEventListener('mouseout', function (e) {
601 | hideTooltip(e.currentTarget);
602 | });
603 | });
604 |
605 | clipboardSnippets.on('success', function (e) {
606 | e.clearSelection();
607 | showTooltip(e.trigger, "Copied!");
608 | });
609 |
610 | clipboardSnippets.on('error', function (e) {
611 | showTooltip(e.trigger, "Clipboard error!");
612 | });
613 | })();
614 |
615 | (function scrollToTop () {
616 | var menuTitle = document.querySelector('.menu-title');
617 |
618 | menuTitle.addEventListener('click', function () {
619 | document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
620 | });
621 | })();
622 |
623 | (function controllMenu() {
624 | var menu = document.getElementById('menu-bar');
625 |
626 | (function controllPosition() {
627 | var scrollTop = document.scrollingElement.scrollTop;
628 | var prevScrollTop = scrollTop;
629 | var minMenuY = -menu.clientHeight - 50;
630 | // When the script loads, the page can be at any scroll (e.g. if you reforesh it).
631 | menu.style.top = scrollTop + 'px';
632 | // Same as parseInt(menu.style.top.slice(0, -2), but faster
633 | var topCache = menu.style.top.slice(0, -2);
634 | menu.classList.remove('sticky');
635 | var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
636 | document.addEventListener('scroll', function () {
637 | scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
638 | // `null` means that it doesn't need to be updated
639 | var nextSticky = null;
640 | var nextTop = null;
641 | var scrollDown = scrollTop > prevScrollTop;
642 | var menuPosAbsoluteY = topCache - scrollTop;
643 | if (scrollDown) {
644 | nextSticky = false;
645 | if (menuPosAbsoluteY > 0) {
646 | nextTop = prevScrollTop;
647 | }
648 | } else {
649 | if (menuPosAbsoluteY > 0) {
650 | nextSticky = true;
651 | } else if (menuPosAbsoluteY < minMenuY) {
652 | nextTop = prevScrollTop + minMenuY;
653 | }
654 | }
655 | if (nextSticky === true && stickyCache === false) {
656 | menu.classList.add('sticky');
657 | stickyCache = true;
658 | } else if (nextSticky === false && stickyCache === true) {
659 | menu.classList.remove('sticky');
660 | stickyCache = false;
661 | }
662 | if (nextTop !== null) {
663 | menu.style.top = nextTop + 'px';
664 | topCache = nextTop;
665 | }
666 | prevScrollTop = scrollTop;
667 | }, { passive: true });
668 | })();
669 | (function controllBorder() {
670 | menu.classList.remove('bordered');
671 | document.addEventListener('scroll', function () {
672 | if (menu.offsetTop === 0) {
673 | menu.classList.remove('bordered');
674 | } else {
675 | menu.classList.add('bordered');
676 | }
677 | }, { passive: true });
678 | })();
679 | })();
680 |
--------------------------------------------------------------------------------
/theme/highlight.js:
--------------------------------------------------------------------------------
1 | /*
2 | Highlight.js 10.1.1 (93fd0d73)
3 | License: BSD-3-Clause
4 | Copyright (c) 2006-2020, Ivan Sagalaev
5 | */
6 | var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""+a(e)+">"}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(/
/g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}());hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}());hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:"?",end:">"}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}());hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin:/,end:/>/},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%\^\+\*]/}]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,i]},a={className:"variable",begin:/\$\([\w-]+\s/,end:/\)/,keywords:{built_in:"subst patsubst strip findstring filter filter-out sort word wordlist firstword lastword dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath error warning shell origin flavor foreach if or and call eval file value"},contains:[i]},r={begin:"^"+e.UNDERSCORE_IDENT_RE+"\\s*(?=[:+?]?=)"},s={className:"section",begin:/^[^\s]+:/,end:/$/,contains:[i]};return{name:"Makefile",aliases:["mk","mak"],keywords:{$pattern:/[\w-]+/,keyword:"define endef undefine ifdef ifndef ifeq ifneq else endif include -include sinclude override export unexport private vpath"},contains:[e.HASH_COMMENT_MODE,i,n,a,r,{className:"meta",begin:/^\.PHONY:/,end:/$/,keywords:{$pattern:/[\.\w]+/,"meta-keyword":".PHONY"}},s]}}}());hljs.registerLanguage("css",function(){"use strict";return function(e){var n={begin:/(?:[A-Z\_\.\-]+|--[a-zA-Z0-9_-]+)\s*:/,returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",begin:/[\w-]+/},{begin:/\(/,end:/\)/,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=\/|'\$]/,contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:/\.[A-Za-z0-9_-]+/},{className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",illegal:/:/,returnBegin:!0,contains:[{className:"keyword",begin:/@\-?\w[\w]*(\-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0},{begin:"{",end:"}",illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,n]}]}}}());hljs.registerLanguage("xml",function(){"use strict";return function(e){var n={className:"symbol",begin:"&[a-z]+;|[0-9]+;|[a-f0-9]+;"},a={begin:"\\s",contains:[{className:"meta-keyword",begin:"#?[a-z_][a-z1-9_-]+",illegal:"\\n"}]},s=e.inherit(a,{begin:"\\(",end:"\\)"}),t=e.inherit(e.APOS_STRING_MODE,{className:"meta-string"}),i=e.inherit(e.QUOTE_STRING_MODE,{className:"meta-string"}),c={endsWithParent:!0,illegal:/,relevance:0,contains:[{className:"attr",begin:"[A-Za-z0-9\\._:-]+",relevance:0},{begin:/=\s*/,relevance:0,contains:[{className:"string",endsParent:!0,variants:[{begin:/"/,end:/"/,contains:[n]},{begin:/'/,end:/'/,contains:[n]},{begin:/[^\s"'=<>`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:"