├── .gitignore ├── theme ├── favicon.png ├── css │ ├── print.css │ ├── general.css │ ├── variables.css │ └── chrome.css ├── highlight.css ├── favicon.svg ├── index.hbs └── book.js ├── book.toml ├── .github ├── workflows │ └── gh-pages.yml └── pull_request_template.md ├── src ├── examples │ ├── unofficial │ │ ├── bdfd.md │ │ ├── README.md │ │ ├── haskell.md │ │ ├── go.md │ │ ├── dart.md │ │ ├── csharp.md │ │ └── rust.md │ ├── python.md │ └── javascript.md ├── api │ ├── reference.md │ └── endpoints.md ├── SUMMARY.md ├── README.md └── legal │ ├── privacy.md │ └── terms.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /theme/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nekos-best/docs/HEAD/theme/favicon.png -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | language = "en" 3 | multilingual = false 4 | title = "nekos.best API" 5 | description = "The official nekos.best API documentation." 6 | 7 | [output.html] 8 | cname = "docs.nekos.best" 9 | 10 | default-theme = "nekosbest" 11 | preferred-dark-theme = "nekosbest" 12 | 13 | git-repository-icon = "fa-github" 14 | git-repository-url = "https://github.com/nekos-best/docs" 15 | 16 | [output.html.search] 17 | limit-results = 10 18 | 19 | [output.html.print] 20 | enable = false 21 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-22.04 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup mdBook 17 | uses: peaceiris/actions-mdbook@v1 18 | with: 19 | mdbook-version: "latest" 20 | 21 | - run: mdbook build 22 | 23 | - name: Deploy 24 | uses: peaceiris/actions-gh-pages@v3 25 | if: ${{ github.ref == 'refs/heads/main' }} 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: ./book 29 | -------------------------------------------------------------------------------- /src/examples/unofficial/bdfd.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # BDFD 4 | 5 | --- 6 | 7 | ## Getting a Image 8 | 9 | ``` 10 | $nomention 11 | $httpGet[https://nekos.best/api/v2/neko] 12 | $title[Here is a Neko for you!] 13 | $description[**Source:** $httpResult[results;0;source_url]] 14 | $image[$httpResult[results;0;url]] 15 | $footer[nekos.best API] 16 | $color[#e91e63] 17 | ``` 18 | 19 | ## Getting a GIF 20 | 21 | ``` 22 | $nomention 23 | $httpGet[https://nekos.best/api/v2/hug] 24 | $description[<@$authorID> hugged <@$mentioned[1]>] 25 | $image[$httpResult[results;0;url]] 26 | $footer[nekos.best API - Anime: $httpResult[results;0;anime_name]] 27 | $color[#e91e63] 28 | ``` 29 | 30 | ## About 31 | 32 | > Example added by: [**oriel beck**](https://github.com/oriel-beck) 33 | -------------------------------------------------------------------------------- /src/api/reference.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | --- 4 | 5 | ## Base URL 6 | 7 | Our base URL is **`https://nekos.best/api/:version`**. Replace `:version` with one of the [available versions below](#versions). 8 | 9 | ## Versions 10 | 11 | | Version | Status | 12 | |----|----| 13 | | **v2** | Current | 14 | 15 | ## Categories 16 | 17 | ### Images (`.png`) 18 | 19 | `husbando`, `kitsune`, `neko`, `waifu` 20 | 21 | ### GIFs (`.gif`) 22 | 23 | `angry`, `baka`, `bite`, `blush`, `bored`, `cry`, `cuddle`, `dance`, `facepalm`, `feed`, `handhold`, `handshake`, `happy`, `highfive`, `hug`, `kick`, `kiss`, `laugh`, `lurk`, `nod`, `nom`, `nope`, `pat`, `peck`, `poke`, `pout`, `punch`, `run`, `shoot`, `shrug`, `slap`, `sleep`, `smile`, `smug`, `stare`, `think`, `thumbsup`, `tickle`, `wave`, `wink`, `yawn`, `yeet` 24 | -------------------------------------------------------------------------------- /src/examples/unofficial/README.md: -------------------------------------------------------------------------------- 1 | # Unofficial Examples 2 | 3 | --- 4 | 5 | This chapter's examples are made by 3rd party contributors. While we check the quality of their examples and libraries during submission, we cannot guarantee their safety or maintenance. 6 | 7 | **nekos.best is not responsible for any damage caused by 3rd party contributors and their example.** 8 | 9 | If there are any issues with the examples or their maintainers, please [open a new issue](https://github.com/nekos-best/docs/issues/new?labels=e-issue,d-issue) on our repository. If you have trouble using an example, contact its maintainer or join the [Discord server](https://nekos.best/discord?ref=docs). 10 | 11 | Do you want to add your own example? Check out our [repository](https://github.com/nekos-best/docs#contributing). 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ## What does this Pull Request change? 6 | 7 | ## I agree that my Pull Request: 8 | 9 | - Follows the [Contribution Guidelines](https://github.com/nekos-best/docs#contribution-guidelines) 10 | - Follows [GitHub's Community Guidelines](https://docs.github.com/en/github/site-policy/github-community-guidelines) 11 | 15 | 16 | ## Any additional notes? 17 | 18 | None 19 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Summary 4 | [Introduction](README.md) 5 | 6 | # API 7 | - [Reference](api/reference.md) 8 | - [Endpoints](api/endpoints.md) 9 | 10 | # Examples 11 | - [JavaScript](examples/javascript.md) 12 | - [Python](examples/python.md) 13 | - [Unofficial Examples](examples/unofficial/README.md) 14 | 15 | - [BDFD](examples/unofficial/bdfd.md) 16 | - [C#](examples/unofficial/csharp.md) 17 | - [Dart](examples/unofficial/dart.md) 18 | - [Go](examples/unofficial/go.md) 19 | - [Haskell](examples/unofficial/haskell.md) 20 | - [Rust](examples/unofficial/rust.md) 21 | 22 | 23 | # Legal 24 | - [Privacy Policy](legal/privacy.md) 25 | - [Terms and Conditions](legal/terms.md) 26 | -------------------------------------------------------------------------------- /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/examples/unofficial/haskell.md: -------------------------------------------------------------------------------- 1 | # Haskell 2 | 3 | --- 4 | 5 | ## Installation 6 | 7 | The [nekos-best](https://hackage.haskell.org/package/nekos-best) package is available on Hackage and can be added to the .cabal file 8 | 9 | ``` 10 | build-depends: 11 | nekos-best ^>= 0.2.0.0 12 | ``` 13 | 14 | ## Usage 15 | 16 | Get a neko image 17 | 18 | ```haskell 19 | import NekosBest.API (getNbImage) 20 | import qualified NekosBest.Category as C 21 | 22 | main = do 23 | res <- getNbImage C.Neko 24 | print res 25 | ``` 26 | 27 | ___ 28 | 29 | For random images use `randomNbImage` passing a `RandomGen` value 30 | 31 | ```haskell 32 | import NekosBest.API (randomNbImage) 33 | import qualified NekosBest.Category as C 34 | 35 | main = do 36 | gen <- getStdGen 37 | (res, gen') <- randomNbImage gen 38 | print res 39 | 40 | ``` 41 | ___ 42 | 43 | Downloading an image 44 | 45 | ```haskell 46 | import NekosBest.API (getNbImage, downloadNbImage) 47 | import qualified NekosBest.Category as C 48 | 49 | main = do 50 | res <- getNbImage C.Neko 51 | mapM_ (\x -> downloadNbImage x "filename") res 52 | ``` 53 | 54 | ## About 55 | 56 | > Example added by: [**xQuantx**](https://github.com/xquantxz) 57 | > 58 | > The source code is available at [**GitHub**](https://github.com/xquantxz/nekos-best.hs) 59 | > 60 | > ![NekosBest](https://img.shields.io/hackage/v/nekos-best.svg) ![NekosBest](https://img.shields.io/github/license/xquantxz/nekos-best%2ehs.svg) ![NekoBest](https://img.shields.io/github/issues/xquantxz/nekos-best%2ehs.svg) -------------------------------------------------------------------------------- /theme/highlight.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 5 | 6 | /* Comment */ 7 | .hljs-comment, 8 | .hljs-quote { 9 | color: #969896; 10 | } 11 | 12 | /* Red */ 13 | .hljs-variable, 14 | .hljs-template-variable, 15 | .hljs-attribute, 16 | .hljs-tag, 17 | .hljs-name, 18 | .hljs-regexp, 19 | .hljs-link, 20 | .hljs-name, 21 | .hljs-selector-id, 22 | .hljs-selector-class { 23 | color: #cc6666; 24 | } 25 | 26 | /* Orange */ 27 | .hljs-number, 28 | .hljs-meta, 29 | .hljs-built_in, 30 | .hljs-builtin-name, 31 | .hljs-literal, 32 | .hljs-type, 33 | .hljs-params { 34 | color: #de935f; 35 | } 36 | 37 | /* Yellow */ 38 | .ruby .hljs-class .hljs-title, 39 | .css .hljs-rule .hljs-attribute { 40 | color: #f0c674; 41 | } 42 | 43 | /* Green */ 44 | .hljs-string, 45 | .hljs-symbol, 46 | .hljs-bullet { 47 | color: #b5bd68; 48 | } 49 | 50 | /* Blue */ 51 | .hljs-title, 52 | .hljs-section { 53 | color: #8abeb7; 54 | } 55 | 56 | /* Purple */ 57 | .hljs-keyword, 58 | .hljs-selector-tag { 59 | color: #b294bb; 60 | } 61 | 62 | .hljs { 63 | display: block; 64 | overflow-x: auto; 65 | background: #1b1b1b; 66 | color: #ffffff; 67 | padding: 0.5em; 68 | } 69 | 70 | .hljs-emphasis { 71 | font-style: italic; 72 | } 73 | 74 | .hljs-strong { 75 | font-weight: bold; 76 | } 77 | 78 | .hljs-addition { 79 | color: #718c00; 80 | } 81 | 82 | .hljs-deletion { 83 | color: #c82829; 84 | } 85 | -------------------------------------------------------------------------------- /theme/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Introduction 4 | 5 | Welcome to [nekos.best](https://nekos.best)'s official documentation. 6 | 7 | --- 8 | 9 | ## About us 10 | 11 | [nekos.best](https://nekos.best) is a [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) API serving fully SFW and high quality anime images and GIFs. 12 | 13 | We focus on giving the best experience for our users and their projects, for free. 14 | 15 | ## Why nekos.best? 16 | 17 | 1. **No authorization required** 18 | 19 | Unlike other APIs, you do not need to authenticate. No Tokens or API keys. 20 | 21 | 2. **Fully SFW** 22 | 23 | Our main focus on nekos.best is providing fully SFW images and GIFs. 24 | 25 | 3. **Crediting creators** 26 | 27 | Respecting creators' work is very important to us, that is why you get everything you need to know about the origin of the image or GIF. 28 | 29 | 4. **Fast and 99.9% uptime** 30 | 31 | The servers are located in the US, so you can be sure to get great response times with a 99.9% uptime. 32 | 33 | 5. **Wide variety of endpoints** 34 | 35 | There are about 48 endpoints and we are going to add many more in the future. 36 | 37 | 6. **CORS enabled** 38 | 39 | For our web developers out there, we have CORS enabled, so you can enjoy using the content on your websites. 40 | 41 | 7. **Community** 42 | 43 | The community is very important for us. We love to get feedback and suggestions to improve our API. 44 | 45 | ## Do you need help? Do you have any suggestions or feedback? 46 | 47 | Join our [Discord server](https://nekos.best/discord?ref=docs) if you need any help. 48 | 49 | We accept suggestions and feedback in the [Discord server](https://nekos.best/discord?ref=docs) and via email [contact@nekos.best](mailto:contact@nekos.best). 50 | 51 | ## Official links 52 | 53 | [Website](https://nekos.best) ・ [Discord server](https://nekos.best/discord?ref=docs) ・ [Status](https://status.nekos.best) ・ [GitHub](https://github.com/nekos-best) 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NekosBest's [documentation](https://docs.nekos.best) 2 | 3 | ## Table of Contents 4 | 5 | 6 | - [Introduction](#introduction) 7 | - [Contributing](#contributing) 8 | - [Contribution Guidelines](#contribution-guidelines) 9 | 10 | ## Introduction 11 | 12 | Welcome to [nekos.best](https://nekos.best)'s documentation repository. 13 | 14 | If you have any questions, feel free to ask them in the [Discord server](https://nekos.best/discord?ref=docs). 15 | 16 | ## Contributing 17 | 18 | Thank you for taking your time to contribute, we really appreciate it. 19 | 20 | Please note that you can contribute only by [adding examples](#adding-an-unofficial-example) and fixing any grammar mistakes. For anything else, please open a [new issue](https://github.com/nekos-best/docs/issues/new?labels=d-issue). 21 | 22 | To contribute, you must [fork this repository](https://docs.github.com/en/get-started/quickstart/fork-a-repo#forking-a-repository) to make any changes and [create a new pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) after you are done. After you create a new pull request, wait patiently until a contributor reviews your pull request. 23 | 24 | ### Contribution Guidelines 25 | 26 | - Your submission may not be submitted in languages other than English. 27 | - Your submission may not contain inappropriate content. 28 | - **Example-specific guidelines:** 29 | - Make sure to document your example with as many details as possible. 30 | - Make sure your code works and is thoroughly tested. 31 | 32 | We reserve the rights to: 33 | 34 | - Make any changes after your submission. 35 | - Remove your submission. 36 | 37 | without any prior notice. 38 | 39 | ### Adding an unofficial example 40 | 41 | 1. Carefully read the [contributing](#contributing) part. 42 | 2. Create a new file under `/src/examples/unofficial` named by its programming language, with the filename ending with `.md` and add your example in there. 43 | 3. Go to [SUMMARY.md](/src/SUMMARY.md#examples) and add its **relative** path under "Unofficial Examples". 44 | -------------------------------------------------------------------------------- /src/examples/python.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [![Python](https://cdn.discordapp.com/emojis/853021892986535957.webp?size=24&quality=lossless)](https://nekos.best/discord?ref=docs) Python 4 | 5 | --- 6 | 7 | #### Using `requests` 8 | 9 | ```py 10 | import requests 11 | 12 | resp = requests.get("https://nekos.best/api/v2/neko") 13 | data = resp.json() 14 | print(data["results"][0]["url"]) 15 | 16 | # https://nekos.best/api/v2/neko/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png 17 | 18 | #### Using `aiohttp` (async) 19 | 20 | ```py 21 | import aiohttp 22 | 23 | async with aiohttp.ClientSession() as session: 24 | async with session.get("https://nekos.best/api/v2/neko") as resp: 25 | data = await resp.json() 26 | print(data["results"][0]["url"]) 27 | 28 | # https://nekos.best/api/v2/neko/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png 29 | ``` 30 | 31 | ## Using our official wrapper 32 | 33 | ### Installation 34 | 35 | Make sure to have pip installed in your environement. It will also install all requirements. 36 | 37 | `pip install -U nekosbest` 38 | 39 | ### Example 40 | 41 | ```py 42 | import asyncio 43 | from typing import Union 44 | 45 | from nekosbest import Client, Result 46 | 47 | client = Client() 48 | 49 | async def get_img(type: str, amount: int = 1) -> Union[Result, list[Result]]: 50 | result = await client.get_image(type, amount) 51 | print(result) 52 | 53 | loop = asyncio.get_event_loop() 54 | 55 | loop.run_until_complete(get_img("neko")) 56 | # 57 | loop.run_until_complete(get_img("neko", 2)) 58 | # [, ] 59 | 60 | ``` 61 | 62 | ### Breaking changes 63 | 64 | #### Migrate from 0.x.x to 1.0.0 65 | 66 | `Client.teardown` has been removed, it is no longer needed to pass it when closing. 67 | 68 | #### Migrate from 1.0.20 to 1.1.0 69 | 70 | `nekosbest.Result.source_details` has been removed. Source details are now in `Result`. 71 | And therefore, `nekosbest.Results` and `nekosbest.SourceDetails` got removed too. 72 | 73 | ## About 74 | 75 | > Created by [**PredaaA**](https://github.com/PredaaA) 76 | > 77 | > The source code is available at [**GitHub**](https://github.com/nekos-best/nekos-best.py) 78 | > 79 | > [![NekosBest](https://img.shields.io/pypi/v/nekosbest?logo=pypi&style=flat-square)](https://pypi.org/project/nekosbest/) [![NekosBest](https://img.shields.io/pypi/dm/nekosbest?color=blue&logo=pypi&style=flat-square)](https://pypi.org/project/nekosbest/) [![NekosBest](https://img.shields.io/github/stars/nekos-best/nekos-best.py?color=yellow&label=Stars&logo=github&style=flat-square)](https://github.com/nekos-best/nekos-best.py) 80 | -------------------------------------------------------------------------------- /src/examples/unofficial/go.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [![CS](https://cdn.discordapp.com/emojis/1128611804596158574.webp?size=24&quality=lossless)](https://nekos.best/discord?ref=docs) Go 4 | 5 | --- 6 | 7 | ## Installation 8 | Add the module to your project 9 | ```bash 10 | $ go get -u github.com/Yakiyo/nekos_best.go 11 | ``` 12 | 13 | ## Usage 14 | Import it in your code 15 | ```go 16 | import ( 17 | nb github.com/Yakiyo/nekos_best.go 18 | ) 19 | ``` 20 | Full reference at [go.pkg.dev](https://pkg.go.dev/github.com/Yakiyo/nekos_best.go) 21 | 22 | ### Fetch a single entry 23 | ```go 24 | res, err := nb.Fetch("neko") 25 | if err != nil { 26 | // handle err 27 | } 28 | fmt.Println(res.Url, res.Artist_name, res.Artist_href) 29 | ``` 30 | 31 | ### Fetch multiple entries 32 | ```go 33 | res, _ := nb.FetchMany("baka", 3) 34 | 35 | fmt.Println(res[0].Url) 36 | ``` 37 | 38 | ### Fetch a file 39 | ```go 40 | import "os" 41 | 42 | res, _ := nb.FetchFile("pat") 43 | 44 | os.WriteFile("image.png", res.Data, 0644) 45 | 46 | // print associated entry for the image 47 | fmt.Println(res.Body) 48 | ``` 49 | 50 | ### Search for an entry 51 | ```go 52 | res, _ := nb.Search("Gochuumon wa Usagi Desuka??", "baka", 3) 53 | 54 | fmt.Println(res) 55 | ``` 56 | 57 | ### Generate random category 58 | ```go 59 | cat := nb.RandomCategory() 60 | ``` 61 | 62 | ## Example without the module 63 | ```go 64 | package main 65 | 66 | import ( 67 | "fmt" 68 | "io" 69 | "net/http" 70 | "encoding/json" 71 | ) 72 | 73 | // Result of an entry for nekos.best api response for 74 | // an image endpoint 75 | type Result struct { 76 | Url string 77 | Artist_name string 78 | Artist_href string 79 | } 80 | 81 | // The entire api response returned 82 | type Api_Response struct { 83 | Results []Result 84 | } 85 | 86 | func main() { 87 | res, err := http.Get("https://nekos.best/api/v2/neko") 88 | if err != nil { 89 | fmt.Println("Error %v", err) 90 | return 91 | } 92 | // Read body to bytes and defer the close 93 | defer res.Body.Close() 94 | bytes, err := io.ReadAll(res.Body) 95 | if err != nil { 96 | fmt.Println("Error %v", err) 97 | return 98 | } 99 | // Parse []byte to json 100 | response := &Api_Response{} 101 | json.Unmarshal(bytes, response) 102 | fmt.Println(response.Results[0]) 103 | } 104 | ``` 105 | 106 | ## About 107 | 108 | > Example added by: [**Yakiyo**](https://github.com/Yakiyo) 109 | > 110 | > Package source code is available on [**Github**](https://github.com/Yakiyo/nekos_best.go) 111 | > 112 | > [![Go Reference](https://pkg.go.dev/badge/github.com/Yakiyo/nekos_best.go.svg)](https://pkg.go.dev/github.com/Yakiyo/nekos_best.go) [![NekosBest](https://img.shields.io/github/stars/Yakiyo/nekos_best.go?color=yellow&label=Stars&logo=github&style=flat-square)](https://github.com/Yakiyo/nekos_best.go) -------------------------------------------------------------------------------- /src/legal/privacy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | > Last updated: March 29, 2022 4 | 5 | This page is used to inform website visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service. 6 | 7 | If you choose to use our Service, then you agree to the collection and use of information in relation with this policy. The Personal Information that we collect are used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at , unless otherwise defined in this Privacy Policy. 10 | 11 | ## Information Collection and Use 12 | 13 | ## Log Data 14 | 15 | We want to inform you that whenever you visit our Service, we collect information that your browser sends to us that is called Log Data. This Log Data may include information such as your computer’s Internet Protocol ("IP") address, browser version, pages of our Service that you visit, the time and date of your visit, the time spent on those pages, and other statistics. 16 | 17 | ## Service Providers 18 | 19 | We may employ third-party companies and individuals due to the following reasons: 20 | 21 | - To facilitate our Service 22 | - To provide the Service on our behalf 23 | - To perform Service-related services 24 | - To assist us in analyzing how our Service is used. 25 | 26 | We want to inform our Service users that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 27 | 28 | ## Security 29 | 30 | We value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and we cannot guarantee its absolute security. 31 | 32 | ## Links to Other Sites 33 | 34 | Our Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by us. Therefore, we strongly advise you to review the Privacy Policy of these websites. We have no control over, and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 35 | 36 | ## Changes to This Privacy Policy 37 | 38 | We reserve the right, at Our sole discretion, to modify or replace the Privacy Policy at any time. 39 | 40 | By continuing to access or use Our Service after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new Privacy Policy, in whole or in part, please stop using the website and the Service. 41 | 42 | ## Contact Us 43 | 44 | If you have any questions about these Privacy Policy, You can contact us: 45 | 46 | - By email: **[contact@nekos.best](mailto:contact@nekos.best)** 47 | -------------------------------------------------------------------------------- /src/examples/unofficial/dart.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [![CS](https://cdn.discordapp.com/emojis/1037721964162330764.webp?size=32&quality=lossless)](https://nekos.best/discord?ref=docs) Dart 4 | 5 | --- 6 | 7 | ## Installation 8 | 9 | Add the Dart [`nekos_best`](https://pub.dev/packages/nekos_best) package to your project: 10 | 11 | ```bash 12 | $ dart pub add nekos_best 13 | ``` 14 | 15 | ## Usage 16 | 17 | Import the package to your project and use the methods. 18 | 19 | ```c 20 | import 'package:nekos_best/nekos_best.dart' show fetch, Client; 21 | 22 | void main() async { 23 | // Use the `fetch` function to fetch a random neko 24 | var neko = await fetch(endpoint: 'neko'); 25 | print(neko); 26 | 27 | // Alternatively you can initiate a `Client` class which 28 | // provides more methods and functions 29 | var client = Client(); 30 | 31 | // `Client.fetch` method 32 | var baka = client.fetch(endpoint: 'baka'); 33 | // `Client.fetch` for a random category 34 | var random = client.fetch(); 35 | // `Client.fetch` with an amount 36 | var five_nekos = client.fetch(endpoint: 'baka', amount: 5); 37 | // `Client.fetchFile` method 38 | var kitsune_file = client.fetchFile('kitsune'); 39 | // `Client.search` method 40 | var search = client.search('Gochuumon wa Usagi Desuka??'); 41 | } 42 | ``` 43 | The package uses [NBResponse](https://pub.dev/documentation/nekos_best/latest/nekos_best/NBResonse-class.html) and [NBBufferResponse](https://pub.dev/documentation/nekos_best/latest/nekos_best/NBBufferResonse-class.html) classes in it's return values. You can access the fields with dot notation. 44 | 45 | ```c 46 | var baka = await client.fetch(endpoint: 'baka'); 47 | // It always returns a List, so 48 | // we need to get the first element from the list 49 | print("url: ${baka[0].url}, source: ${baka[0].anime_name}"); 50 | ``` 51 | See [example/struct_example.dart](https://github.com/Yakiyo/nekos_best_dart/blob/main/example/structs_example.dart) for details and consult the [Api Reference](https://pub.dev/documentation/nekos_best/latest/nekos_best/nekos_best-library.html) 52 | 53 | 54 | More details on the package can be found in the repo's [README](https://github.com/Yakiyo/nekos_best_dart#readme). Examples on all methods, usage, classes are in the [example](https://github.com/Yakiyo/nekos_best_dart/tree/main/example) directory. 55 | 56 | ### Example without wrapper 57 | 58 | Add [http](https://pub.dev/packages/http) to your project and import it. 59 | 60 | ```c 61 | import 'package:http/http.dart' as http; 62 | import 'dart:convert' as convert; 63 | 64 | void main() async { 65 | var url = Uri.parse('https://nekos.best/api/v2/neko'); 66 | var res = await http.get(url); 67 | var neko = convert.jsonDecode(res.body) as Map; 68 | print(neko['results'][0]); 69 | } 70 | ``` 71 | 72 | ## About 73 | 74 | > Example added by: [**Yakiyo**](https://github.com/Yakiyo) 75 | > 76 | > Package source code is available on [**Github**](https://github.com/Yakiyo/nekos_best_dart) 77 | > 78 | > [![NekosBest](https://img.shields.io/pub/v/nekos_best?logo=dart&style=flat-square&label=Version)](https://pub.dev/packages/nekos_best) [![NekosBest](https://img.shields.io/pub/popularity/nekos_best?color=blue&logo=dart&style=flat-square&label=Popularity)](https://pub.dev/packages/nekos_best) [![NekosBest](https://img.shields.io/github/stars/Yakiyo/nekos_best_dart?color=yellow&label=Stars&logo=github&style=flat-square)](https://github.com/Yakiyo/nekos_best_dart) 79 | -------------------------------------------------------------------------------- /src/api/endpoints.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Endpoints 4 | 5 | --- 6 | 7 | ## GET `/endpoints` 8 | 9 | Lists all available categories. 10 | 11 | > **Example:** 12 | > 13 | > **Returns:** 14 | > 15 | > ```json 16 | > { 17 | > "neko": {"format": "png"}, 18 | > ···, 19 | > "wink": {"format": "gif"} 20 | > } 21 | > ``` 22 | 23 | ## GET `/:category?amount=X` 24 | 25 | Gets a random image or GIF from the available [categories](reference.md#categories) along with its metadata. 26 | 27 | The amount query may be used to retrieve multiple assets at once. The amount is a number such that `1 ≤ X ≤ 20`. 28 | 29 | > **Example:** 30 | > 31 | > **Returns:** 32 | > 33 | > ```json 34 | >{ 35 | > "results":[ 36 | > { 37 | > "artist_href":"https://www.pixiv.net/en/users/47065875", 38 | > "artist_name":"かえで", 39 | > "source_url":"https://www.pixiv.net/en/artworks/88682108", 40 | > "url":"https://nekos.best/api/v2/neko/bbffa4e8-dc40-11ed-afa1-0242ac120002.png" 41 | > } 42 | > ] 43 | >} 44 | > ``` 45 | 46 | > **Example:** 47 | > 48 | > **Returns:** 49 | > 50 | > ```json 51 | >{ 52 | > "results":[ 53 | > { 54 | > "anime_name":"Sword Art Online", 55 | > "url":"https://nekos.best/api/v2/hug/c6a7d384-dc40-11ed-afa1-0242ac120002.gif" 56 | > }, 57 | > { 58 | > "anime_name":"Hibike! Euphonium", 59 | > "url":"https://nekos.best/api/v2/hug/ca26cfba-dc40-11ed-afa1-0242ac120002.gif" 60 | > } 61 | > ] 62 | >} 63 | > ``` 64 | 65 | ## GET `/search?query=X&type=X&category=X&amount=X` 66 | 67 | The query parameter can be used to search for a specific phrase in the image or GIF source. Use the type query to get **`1` images** or **`2` GIFs** results. 68 | 69 | \* Optional parameters: Use the category query for getting images or GIFs from a specific endpoint. The amount query may be used to retrieve multiple results at once. 70 | 71 | > **Example:** 72 | > 73 | > **Returns:** 74 | > 75 | > ```json 76 | >{ 77 | > "results":[ 78 | > { 79 | > "artist_href":"https://twitter.com/Aramarufox/", 80 | > "artist_name":"Aramarufox", 81 | > "source_url":"https://twitter.com/Aramarufox/status/1491744680999940101", 82 | > "url":"https://nekos.best/api/v2/kitsune/ca26cfba-dc40-11ed-afa1-0242ac120002.png" 83 | > } 84 | > ] 85 | >} 86 | >``` 87 | 88 | > **Example:** 89 | > 90 | > **Returns:** 91 | > 92 | > ```json 93 | >{ 94 | > "results":[ 95 | > { 96 | > "anime_name":"Sewayaki Kitsune no Senko-san", 97 | > "url":"https://nekos.best/api/v2/pat/ca26cfba-dc40-11ed-afa1-0242ac120002.gif" 98 | > }, 99 | > { 100 | > "anime_name":"Sewayaki Kitsune no Senko-san", 101 | > "url":"https://nekos.best/api/v2/pat/ca26cfba-dc40-11ed-afa1-0242ac120002.gif" 102 | > } 103 | > ] 104 | >} 105 | >``` 106 | 107 | ## GET `/:category/:filename.:format` 108 | 109 | Gets a specific image from our categories. Replace `:filename` with the asset's filename and `:format` with the category's format. 110 | 111 | **Note:** The asset's metadata are provided URL-encoded, in the response's headers under `anime_name`, `artist_name`, `artist_href` and `source_url`. 112 | 113 | > **Example:** 114 | > 115 | > **Returns:** *Binary data* 116 | -------------------------------------------------------------------------------- /src/examples/unofficial/csharp.md: -------------------------------------------------------------------------------- 1 | # [![CS](https://cdn.discordapp.com/emojis/972378383147208744.webp?size=32&quality=lossless)](https://nekos.best/discord?ref=docs) C\# 2 | 3 | --- 4 | 5 | ## Installation 6 | 7 | Add the NuGet package `NekosBestApiNet` to your project: 8 | 9 | ```ps 10 | dotnet add package NekosBestApiNet 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```cs 16 | using System; 17 | using System.Threading.Tasks; 18 | using NekosBestApiNet; 19 | 20 | public class ExampleClass 21 | { 22 | private readonly NekosBestApi _nekosBestApi; 23 | 24 | public ExampleClass() 25 | => _nekosBestApi = new NekosBestApi(); 26 | 27 | public async Task Hug() 28 | { 29 | var image = await _nekosBestApi.ActionsApi.Hug(); 30 | 31 | Console.WriteLine(image.Results.FirstOrDefault().Url); 32 | } 33 | } 34 | ``` 35 | 36 | ## Example using custom HttpClient 37 | 38 | You can provide your own HttpClient instance, but you have to set the BaseAddress manually 39 | 40 | ```cs 41 | using System; 42 | using System.Net.Http; 43 | using System.Net.Http.Headers; 44 | using System.Threading.Tasks; 45 | using NekosBestApiNet; 46 | 47 | class ExampleClass 48 | { 49 | private NekosBestApi _nekosBestApi; 50 | 51 | public ExampleClass() 52 | { 53 | var httpClient = new HttpClient 54 | { 55 | BaseAddress = new Uri("https://nekos.best/api/v2") 56 | }; 57 | 58 | _nekosBestApi = new NekosBestApi(httpClient); 59 | } 60 | } 61 | ``` 62 | 63 | ## Example using dependency injection 64 | 65 | Create a ServiceCollection, then add an instance of the NekosBestApi class to it 66 | 67 | ```cs 68 | using System; 69 | using System.Threading.Tasks; 70 | using NekosBestApiNet; 71 | using Microsoft.Extensions.DependencyInjection; 72 | 73 | public class Startup 74 | { 75 | private NekosBestApi _nekosBestApi; 76 | 77 | private IServiceProvider _serviceProvider; 78 | 79 | public void Init() 80 | { 81 | var services = new ServiceCollection(); 82 | 83 | _nekosBestApi = new NekosBestApi(); 84 | 85 | ConfigureServices(services); 86 | _serviceProvider = services.BuildServiceProvider(); 87 | } 88 | 89 | public async Task RunAsync() 90 | { 91 | var exampleClass = _serviceProvider.GetService(); 92 | 93 | var image = await _nekosBestApi.ActionsApi.Hug(); 94 | 95 | Console.WriteLine(image.Results.FirstOrDefault().Url); 96 | } 97 | 98 | private void ConfigureServices(IServiceCollection services) 99 | { 100 | services.AddSingleton(_nekosBestApi); 101 | services.AddSingleton(); 102 | } 103 | } 104 | ``` 105 | 106 | ### Using this in a class 107 | 108 | ```cs 109 | using System.Threading.Tasks; 110 | using NekosBestApiNet; 111 | using NekosBestApiNet.Models.Images; 112 | 113 | public class ExampleClass 114 | { 115 | private readonly NekosBestApi _nekosBestApi; 116 | 117 | public ExampleClass(NekosBestApi nekosBestApi) 118 | => _nekosBestApi = nekosBestApi; 119 | 120 | public async Task Hug() 121 | => await _nekosBestApi.Hug(); 122 | } 123 | ``` 124 | 125 | ## About 126 | 127 | > Example added by: [**SylveonDeko**](https://github.com/SylveonDeko) 128 | > 129 | > The source code is available at [**GitHub**](https://github.com/Sylveon76/Nekos.Best-API) 130 | > 131 | > [![NekosBest](https://img.shields.io/nuget/v/NekosBestApiNet?color=white&logo=nuget&style=flat-square)](https://www.nuget.org/packages/NekosBestApiNet/) [![NekosBest](https://img.shields.io/nuget/dt/NekosBestApiNet?color=white&logo=nuget&style=flat-square)](https://www.nuget.org/packages/NekosBestApiNet/) [![NekosBest](https://img.shields.io/github/stars/Sylveon76/Nekos.Best-API?color=yellow&label=Stars&logo=github&style=flat-square)](https://github.com/Sylveon76/Nekos.Best-API) 132 | -------------------------------------------------------------------------------- /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 | -webkit-text-size-adjust: none; 16 | } 17 | 18 | body { 19 | margin: 0; 20 | font-size: 1.6rem; 21 | overflow-x: hidden; 22 | } 23 | 24 | code { 25 | font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; 26 | font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ 27 | } 28 | 29 | /* Don't change font size in headers. */ 30 | h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { 31 | font-size: unset; 32 | } 33 | 34 | .left { float: left; } 35 | .right { float: right; } 36 | .boring { opacity: 0.6; } 37 | .hide-boring .boring { display: none; } 38 | .hidden { display: none !important; } 39 | 40 | h2, h3 { margin-top: 2.5em; } 41 | h4, h5 { margin-top: 2em; } 42 | 43 | .header + .header h3, 44 | .header + .header h4, 45 | .header + .header h5 { 46 | margin-top: 1em; 47 | } 48 | 49 | h1:target::before, 50 | h2:target::before, 51 | h3:target::before, 52 | h4:target::before, 53 | h5:target::before, 54 | h6:target::before { 55 | display: inline-block; 56 | content: "»"; 57 | margin-left: -30px; 58 | width: 30px; 59 | } 60 | 61 | /* This is broken on Safari as of version 14, but is fixed 62 | in Safari Technology Preview 117 which I think will be Safari 14.2. 63 | https://bugs.webkit.org/show_bug.cgi?id=218076 64 | */ 65 | :target { 66 | scroll-margin-top: calc(var(--menu-bar-height) + 0.5em); 67 | } 68 | 69 | .page { 70 | outline: 0; 71 | padding: 0 var(--page-padding); 72 | margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */ 73 | } 74 | .page-wrapper { 75 | box-sizing: border-box; 76 | } 77 | .js:not(.sidebar-resizing) .page-wrapper { 78 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 79 | } 80 | 81 | .content { 82 | overflow-y: auto; 83 | padding: 0 15px; 84 | padding-bottom: 50px; 85 | } 86 | .content main { 87 | margin-left: auto; 88 | margin-right: auto; 89 | max-width: var(--content-max-width); 90 | } 91 | .content p { line-height: 1.45em; } 92 | .content ol { line-height: 1.45em; } 93 | .content ul { line-height: 1.45em; } 94 | .content a { text-decoration: none; } 95 | .content a:hover { text-decoration: underline; } 96 | .content img, .content video { max-width: 100%; } 97 | .content .header:link, 98 | .content .header:visited { 99 | color: var(--fg); 100 | } 101 | .content .header:link, 102 | .content .header:visited:hover { 103 | text-decoration: none; 104 | } 105 | 106 | table { 107 | margin: 0 auto; 108 | border-collapse: collapse; 109 | } 110 | table td { 111 | padding: 3px 20px; 112 | border: 1px var(--table-border-color) solid; 113 | } 114 | table thead { 115 | background: var(--table-header-bg); 116 | } 117 | table thead td { 118 | font-weight: 700; 119 | border: none; 120 | } 121 | table thead th { 122 | padding: 3px 20px; 123 | } 124 | table thead tr { 125 | border: 1px var(--table-header-bg) solid; 126 | } 127 | /* Alternate background colors for rows */ 128 | table tbody tr:nth-child(2n) { 129 | background: var(--table-alternate-bg); 130 | } 131 | 132 | 133 | blockquote { 134 | margin: 20px 0; 135 | padding: 0 20px; 136 | color: var(--fg); 137 | background-color: var(--quote-bg); 138 | border-top: .1em solid var(--quote-border); 139 | border-bottom: .1em solid var(--quote-border); 140 | } 141 | 142 | 143 | :not(.footnote-definition) + .footnote-definition, 144 | .footnote-definition + :not(.footnote-definition) { 145 | margin-top: 2em; 146 | } 147 | .footnote-definition { 148 | font-size: 0.9em; 149 | margin: 0.5em 0; 150 | } 151 | .footnote-definition p { 152 | display: inline; 153 | } 154 | 155 | .tooltiptext { 156 | position: absolute; 157 | visibility: hidden; 158 | color: #fff; 159 | background-color: #333; 160 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ 161 | left: -8px; /* Half of the width of the icon */ 162 | top: -35px; 163 | font-size: 0.8em; 164 | text-align: center; 165 | border-radius: 6px; 166 | padding: 5px 8px; 167 | margin: 5px; 168 | z-index: 1000; 169 | } 170 | .tooltipped .tooltiptext { 171 | visibility: visible; 172 | } 173 | 174 | .chapter li.part-title { 175 | color: var(--sidebar-fg); 176 | margin: 5px 0px; 177 | font-weight: bold; 178 | } 179 | 180 | .result-no-output { 181 | font-style: italic; 182 | } 183 | -------------------------------------------------------------------------------- /theme/css/variables.css: -------------------------------------------------------------------------------- 1 | /* Globals */ 2 | 3 | :root { 4 | --sidebar-width: 250px; 5 | --page-padding: 15px; 6 | --content-max-width: 750px; 7 | --menu-bar-height: 50px; 8 | } 9 | 10 | /* Themes */ 11 | 12 | .nekosbest { 13 | --bg: hsl(210, 20%, 5%); 14 | --fg: #d6d6d6; 15 | 16 | --sidebar-bg: hsl(210, 10%, 10%); 17 | --sidebar-fg: #c8c9db; 18 | --sidebar-non-existant: #5c6773; 19 | --sidebar-active: #e91e63; 20 | --sidebar-spacer: #2d334f; 21 | 22 | --scrollbar: #e91e63; 23 | 24 | --icons: #e91e63; 25 | --icons-hover: #fc90b4; 26 | 27 | --links: #e91e63; 28 | 29 | --inline-code-color: #e91e63; 30 | 31 | --theme-popup-bg: #14191f; 32 | --theme-popup-border: #e91e63; 33 | --theme-hover: #191f26; 34 | 35 | --quote-bg: hsl(210, 10%, 15%); 36 | --quote-border: #e91e63; 37 | 38 | --table-border-color: hsl(210, 25%, 13%); 39 | --table-header-bg: hsl(200, 10%, 20%); 40 | --table-alternate-bg: hsl(210, 20%, 20%); 41 | 42 | --searchbar-border-color: #848484; 43 | --searchbar-bg: #424242; 44 | --searchbar-fg: #fff; 45 | --searchbar-shadow-color: #d4c89f; 46 | --searchresults-header-fg: #666; 47 | --searchresults-border-color: #e91e63; 48 | --searchresults-li-bg: #252932; 49 | --search-mark-bg: #ff6699; 50 | } 51 | 52 | .light { 53 | --bg: hsl(0, 0%, 100%); 54 | --fg: hsl(0, 0%, 0%); 55 | 56 | --sidebar-bg: #fafafa; 57 | --sidebar-fg: hsl(0, 0%, 0%); 58 | --sidebar-non-existant: #aaaaaa; 59 | --sidebar-active: #1f1fff; 60 | --sidebar-spacer: #f4f4f4; 61 | 62 | --scrollbar: #8F8F8F; 63 | 64 | --icons: #747474; 65 | --icons-hover: #000000; 66 | 67 | --links: #20609f; 68 | 69 | --inline-code-color: #ffffff; 70 | 71 | --theme-popup-bg: #fafafa; 72 | --theme-popup-border: #cccccc; 73 | --theme-hover: #e6e6e6; 74 | 75 | --quote-bg: hsl(197, 37%, 96%); 76 | --quote-border: hsl(197, 37%, 91%); 77 | 78 | --table-border-color: hsl(0, 0%, 95%); 79 | --table-header-bg: hsl(0, 0%, 80%); 80 | --table-alternate-bg: hsl(0, 0%, 97%); 81 | 82 | --searchbar-border-color: #aaa; 83 | --searchbar-bg: #fafafa; 84 | --searchbar-fg: #000; 85 | --searchbar-shadow-color: #aaa; 86 | --searchresults-header-fg: #666; 87 | --searchresults-border-color: #888; 88 | --searchresults-li-bg: #e4f2fe; 89 | --search-mark-bg: #a2cff5; 90 | } 91 | 92 | .navy { 93 | --bg: hsl(226, 23%, 11%); 94 | --fg: #bcbdd0; 95 | 96 | --sidebar-bg: #282d3f; 97 | --sidebar-fg: #c8c9db; 98 | --sidebar-non-existant: #505274; 99 | --sidebar-active: #2b79a2; 100 | --sidebar-spacer: #2d334f; 101 | 102 | --scrollbar: var(--sidebar-fg); 103 | 104 | --icons: #737480; 105 | --icons-hover: #b7b9cc; 106 | 107 | --links: #2b79a2; 108 | 109 | --inline-code-color: #c5c8c6; 110 | 111 | --theme-popup-bg: #161923; 112 | --theme-popup-border: #737480; 113 | --theme-hover: #282e40; 114 | 115 | --quote-bg: hsl(226, 15%, 17%); 116 | --quote-border: hsl(226, 15%, 22%); 117 | 118 | --table-border-color: hsl(226, 23%, 16%); 119 | --table-header-bg: hsl(226, 23%, 31%); 120 | --table-alternate-bg: hsl(226, 23%, 14%); 121 | 122 | --searchbar-border-color: #aaa; 123 | --searchbar-bg: #aeaec6; 124 | --searchbar-fg: #000; 125 | --searchbar-shadow-color: #aaa; 126 | --searchresults-header-fg: #5f5f71; 127 | --searchresults-border-color: #5c5c68; 128 | --searchresults-li-bg: #242430; 129 | --search-mark-bg: #a2cff5; 130 | } 131 | 132 | 133 | @media (prefers-color-scheme: dark) { 134 | .light.no-js { 135 | --bg: hsl(200, 7%, 8%); 136 | --fg: #98a3ad; 137 | 138 | --sidebar-bg: #292c2f; 139 | --sidebar-fg: #a1adb8; 140 | --sidebar-non-existant: #505254; 141 | --sidebar-active: #3473ad; 142 | --sidebar-spacer: #393939; 143 | 144 | --scrollbar: var(--sidebar-fg); 145 | 146 | --icons: #43484d; 147 | --icons-hover: #b3c0cc; 148 | 149 | --links: #2b79a2; 150 | 151 | --inline-code-color: #c5c8c6; 152 | 153 | --theme-popup-bg: #141617; 154 | --theme-popup-border: #43484d; 155 | --theme-hover: #1f2124; 156 | 157 | --quote-bg: hsl(234, 21%, 18%); 158 | --quote-border: hsl(234, 21%, 23%); 159 | 160 | --table-border-color: hsl(200, 7%, 13%); 161 | --table-header-bg: hsl(200, 7%, 28%); 162 | --table-alternate-bg: hsl(200, 7%, 11%); 163 | 164 | --searchbar-border-color: #aaa; 165 | --searchbar-bg: #b7b7b7; 166 | --searchbar-fg: #000; 167 | --searchbar-shadow-color: #aaa; 168 | --searchresults-header-fg: #666; 169 | --searchresults-border-color: #98a3ad; 170 | --searchresults-li-bg: #2b2b2f; 171 | --search-mark-bg: #355c7d; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/examples/javascript.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # [![JavaScript](https://cdn.discordapp.com/emojis/853021893070159882.webp?size=24&quality=lossless)](https://nekos.best/discord?ref=docs) JavaScript 4 | 5 | --- 6 | 7 | #### Using `fetch` 8 | 9 | ```js 10 | fetch('https://nekos.best/api/v2/neko') 11 | .then(response => response.json()) 12 | .then(json => console.log(json.results[0].url)) 13 | 14 | // https://nekos.best/api/v2/neko/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png 15 | ``` 16 | 17 | #### Using `async` 18 | 19 | ```js 20 | async function getNeko() { 21 | const response = await fetch('https://nekos.best/api/v2/neko') 22 | const json = await response.json() 23 | console.log(json.results[0].url) 24 | } 25 | 26 | await getNeko() 27 | 28 | // https://nekos.best/api/v2/neko/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png 29 | ``` 30 | 31 | ## Using our official wrapper 32 | 33 | ### Installation 34 | 35 | `npm install nekos-best.js` | `yarn add nekos-best.js` | `pnpm install nekos-best.js` 36 | 37 | ### Usage 38 | 39 | ```js 40 | import { Client, fetchRandom } from "nekos-best.js"; 41 | 42 | // You can use the `fetchRandom()` function to fetch a random neko. 43 | console.log(await fetchRandom("neko")); // { results: [{ artist_href: '···', artist_name: '···', source_url: '···', url: 'https://nekos.best/api/v2/neko/XXXXXX-XXXXX.png' }] } 44 | 45 | // Alternatively, you can initialize a new client which offers more features. 46 | const nekosBest = new Client(); 47 | 48 | // Such as the `.fetch()` method. 49 | console.log(await nekosBest.fetch("neko", 1)); // { results: [{ artist_href: '···', artist_name: '···', source_url: '···', url: 'https://nekos.best/api/v2/neko/XXXXXX-XXXXX.png' }] } 50 | console.log(await nekosBest.fetch("hug", 10)); // { results: [{ artist_href: '···', artist_name: '···', source_url: '···', url: 'https://nekos.best/api/v2/hug/XXXXXX-XXXXX.gif' }, ···] } 51 | 52 | // Or the `.fetchFile()` method to get a single file. 53 | console.log(await nekosBest.fetchFile("neko")); // { artist_href: '···', ···, data: } 54 | ``` 55 | 56 | #### Build a simple Discord Bot with [`discord.js`](https://www.npmjs.com/package/discord.js) 57 | 58 | ```js 59 | import { Client as DiscordClient } from "discord.js"; 60 | import { Client } from "nekos-best.js"; 61 | 62 | const discordClient = new DiscordClient(); 63 | const nekosBest = new Client(); 64 | 65 | discordClient.on("messageCreate", async (message) => { 66 | if (message.author.bot) return; 67 | 68 | if (message.content.startsWith('!neko')) { 69 | message.channel.send((await nekosBest.fetch("neko", 1)).results[0].url); 70 | } 71 | }) 72 | 73 | discordClient.login("************************.******.***************************"); 74 | ``` 75 | 76 | ## Migrate from 5.X.X to 6.X.X 77 | 78 | **❗ For the TypeScript users, the type `NbEndpointMetadata` will be removed in the 7.X.X version due to recent API changes** 79 | 80 | ### `.fetchRandom()` & `.fetchMultiple()` methods have been removed in favor of the `.fetch(category, amount)` method 81 | 82 | ```diff 83 | const nekosBest = new Client(); 84 | 85 | - nekosBest.fetchRandom("neko") 86 | + nekosBest.fetch("neko", 1) 87 | ``` 88 | 89 | ```diff 90 | const nekosBest = new Client(); 91 | 92 | - nekosBest.fetchMultiple("neko", 15) 93 | + nekosBest.fetch("neko", 15) 94 | ``` 95 | 96 | ### The `.init()` method has been removed 97 | 98 | ```diff 99 | const nekosBest = new Client(); 100 | 101 | - await nekosBest.init(); 102 | ``` 103 | 104 | ## Migrate from 4.X.X to 5.X.X 105 | 106 | #### The `fetchNeko(category)` function has been removed in favor of the `.fetchRandom()` method and its shortcut `fetchRandom()` 107 | 108 | ```diff 109 | - fetchNeko('category') 110 | + const nekosBest = new Client(); 111 | + 112 | + nekosBest.fetchRandom('category') 113 | ``` 114 | 115 | ```diff 116 | - fetchNeko('category') 117 | + fetchRandom('category') 118 | ``` 119 | 120 | #### The optional parameter `amount` of the `fetchNeko()` function has been removed in favor of the `.fetchMultiple()` method 121 | 122 | ```diff 123 | - fetchNeko('category', 15) 124 | + const nekosBest = new Client(); 125 | + 126 | + nekosBest.fetchMultiple('category', 15) 127 | ``` 128 | 129 | ### Other Changes 130 | 131 | - The optional options `max` and `min` of the `fetchNeko()` function have been removed 132 | 133 | ## About 134 | 135 | > Created by [**Thunder04**](https://github.com/Thunder04) 136 | > 137 | > The source code is available at [**GitHub**](https://github.com/nekos-best/nekos-best.js) 138 | > 139 | > [![NekosBest](https://img.shields.io/npm/v/nekos-best.js?color=red&logo=npm&style=flat-square)](https://www.npmjs.com/package/nekos-best.js) [![NekosBest](https://img.shields.io/npm/dm/nekos-best.js?color=red&logo=npm&style=flat-square)](https://www.npmjs.com/package/nekos-best.js) [![NekosBest](https://img.shields.io/github/stars/nekos-best/nekos-best.js?color=yellow&label=Stars&logo=github&style=flat-square)](https://github.com/nekos-best/nekos-best.js) 140 | -------------------------------------------------------------------------------- /src/examples/unofficial/rust.md: -------------------------------------------------------------------------------- 1 | # [![Rust](https://cdn.discordapp.com/emojis/972378381867941928.webp?size=24&quality=lossless)](https://nekos.best/discord?ref=docs) Rust 2 | 3 | --- 4 | 5 | 6 | ## Usage 7 | 8 | ```toml 9 | [dependencies] 10 | nekosbest = "0.21" 11 | ``` 12 | 13 | ## Example 14 | 15 | ```rust, noplaypen 16 | #[tokio::main] 17 | async fn main() -> Result<(), Box> { 18 | let img_url: String = nekosbest::get(nekosbest::Category::Neko).await?.url; 19 | println!("{img_url}"); 20 | Ok(()) 21 | } 22 | ``` 23 | 24 | Or with an amount (amount is capped at 20 by the server): 25 | 26 | ```rust, noplaypen 27 | #[tokio::main] 28 | async fn main() -> Result<(), Box> { 29 | let images = nekosbest::get_amount(nekosbest::Category::Neko, 20).await?.0; 30 | println!("{images:?}"); 31 | Ok(()) 32 | } 33 | ``` 34 | 35 | Or if you already have a `Client` that you want to use, 36 | use `get_with_client` and `get_with_client_amount` respectively: 37 | 38 | ```rust, noplaypen 39 | #[tokio::main] 40 | async fn main() -> Result<(), Box> { 41 | let client = nekosbest::client::Client::new(nekosbest::client::ClientConfig::default()); 42 | let details = nekosbest::get_with_client(&client, nekosbest::Category::Neko) 43 | .await? 44 | .details 45 | .try_into_image() 46 | .unwrap(); 47 | println!("Source: {}", details.source_url); 48 | println!("Artist: {}", details.artist_name); 49 | println!("Artist link: {}", details.artist_href); 50 | Ok(()) 51 | } 52 | ``` 53 | 54 | There is another property called `details`: 55 | 56 | For `Category::Neko`, `Category::Husbando`, `Category::Kitsune`, `Category::Waifu` (image endpoints): 57 | 58 | ```rust, noplaypen 59 | #[tokio::main] 60 | async fn main() -> Result<(), Box> { 61 | let details = nekosbest::get(nekosbest::Category::Neko) 62 | .await? 63 | .details 64 | .try_into_image() 65 | .unwrap(); 66 | println!("Source: {}", details.source_url); 67 | println!("Artist: {}", details.artist_name); 68 | println!("Artist link: {}", details.artist_href); 69 | Ok(()) 70 | } 71 | ``` 72 | 73 | For everything else (GIF endpoints): 74 | 75 | ```rust, noplaypen 76 | #[tokio::main] 77 | async fn main() -> Result<(), Box> { 78 | let details = nekosbest::get(nekosbest::Category::Pat) 79 | .await? 80 | .details 81 | .try_into_gif() 82 | .unwrap(); 83 | println!("Anime name: {}", details.anime_name); 84 | Ok(()) 85 | } 86 | ``` 87 | 88 | Or with the `strong-types` feature, bringing strong types guarantees for details, so no `unwrap` / `expect` for the details type: 89 | 90 | **Warning**: Experimental, may change at any point in the future. 91 | 92 | Remember to add the `st_` in front of `get`, `get_amount`, `get_with_client` and `get_with_client_amount`. 93 | 94 | Neko: 95 | 96 | ```rust, noplaypen 97 | #[tokio::main] 98 | async fn main() -> Result<(), Box> { 99 | let resp = nekosbest::st_get::().await?; 100 | let details = resp.details(); 101 | println!("Artist: {}", details.artist_name); 102 | println!("Artist link: {}", details.artist_href); 103 | println!("Source: {}", details.source_url); 104 | Ok(()) 105 | } 106 | ``` 107 | 108 | GIF: 109 | 110 | ```rust, noplaypen 111 | #[tokio::main] 112 | async fn main() -> Result<(), Box> { 113 | let details = nekosbest::st_get::().await?.details; 114 | println!("Anime name: {}", details.anime_name); 115 | Ok(()) 116 | } 117 | ``` 118 | 119 | ## Downloading the images. 120 | 121 | With the `download` feature, you can download the images directly, like so: 122 | 123 | ```rust, noplaypen 124 | #[tokio::main] 125 | async fn main() -> Result<(), Box> { 126 | let resp = nekosbest::get(nekosbest::Category::Neko).await?; 127 | let image = nekosbest::download::download(&resp).await?; 128 | 129 | // maybe also save, or just use it directly 130 | tokio::task::spawn_blocking(move || image.save("neko.png")).await??; 131 | 132 | // or alternatively, if you just want to save it, without 133 | // loading the whole image in-memory: 134 | nekosbest::download::download_to_file(&resp, "neko.png").await?; 135 | Ok(()) 136 | } 137 | ``` 138 | 139 | Or directly from a given URL: 140 | 141 | ```rust, noplaypen 142 | #[tokio::main] 143 | async fn main() -> Result<(), Box> { 144 | // let resp = nekosbest::get(nekosbest::Category::Neko).await?; 145 | // let url: String = resp.url; 146 | let url = "https://nekos.best/api/v2/neko/1efcda2d-d0d3-4e96-9b40-86852374b4bc.png".to_owned(); 147 | let image = nekosbest::download::download_from_url(&url).await?; 148 | tokio::task::spawn_blocking(move || image.save("neko.png")).await??; 149 | 150 | // or alternatively, if you just want to save it, without 151 | // loading the whole image in-memory: 152 | nekosbest::download::download_from_url_to_file(&url, "neko.png").await?; 153 | Ok(()) 154 | } 155 | ``` 156 | 157 | ## Blocking client 158 | 159 | All functions become blocking when used with the "blocking" feature. 160 | 161 | ## About 162 | 163 | > Example added by: [**dnbln**](https://github.com/dnbln) 164 | > 165 | > The source code is available at [**GitHub**](https://github.com/dnbln/nb-rs) 166 | > 167 | > [![NekosBest](https://img.shields.io/crates/v/nekosbest?style=flat-square)](https://crates.io/crates/nekosbest) [![NekosBest](https://img.shields.io/crates/d/nekosbest?color=orange&style=flat-square)](https://crates.io/crates/nekosbest) [![NekosBest](https://img.shields.io/github/stars/dnbln/nb-rs?color=yellow&label=Stars&logo=github&style=flat-square)](https://github.com/dnbln/nb-rs) -------------------------------------------------------------------------------- /src/legal/terms.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Terms and Conditions 4 | 5 | > Last updated: November 17, 2024 6 | 7 | ## Interpretation and Definitions 8 | 9 | ### Interpretation 10 | 11 | The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural. 12 | 13 | ### Definitions 14 | 15 | For the purposes of these Terms and Conditions: 16 | 17 | **Affiliate** 18 | 19 | *Means an entity that controls, is controlled by or is under common control with a party, where "control" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.* 20 | 21 | **Country** 22 | 23 | *Refers to: United States of America (USA)* 24 | 25 | **Company** 26 | 27 | *Referred to as either "the Company", "We", "Us" or "Our" in this Agreement: Refers to NEKOSBEST.* 28 | 29 | **Device** 30 | 31 | *Means any device that can access the Service such as a computer, a cellphone or a digital tablet.* 32 | 33 | **Service** 34 | 35 | *Refers to this Website.* 36 | 37 | **Website** 38 | 39 | *Refers to "NEKOSBEST", accessible from * 40 | 41 | **You** 42 | 43 | *Means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.* 44 | 45 | ### Acknowledgment 46 | 47 | These are the Terms and Conditions governing the use of this Service and the agreement that operates between You and the Company. These Terms and Conditions set out the rights and obligations of all users regarding the use of the Service. 48 | 49 | Your access to and use of the Service is conditioned on Your acceptance of and compliance with these Terms and Conditions. These Terms and Conditions apply to all visitors, users and others who access or use the Service. 50 | 51 | By accessing or using the Service You agree to be bound by these Terms and Conditions. If You disagree with any part of these Terms and Conditions then You may not access the Service. 52 | 53 | Your access to and use of the Service is also conditioned on Your acceptance of and compliance with the Privacy Policy of the Company. Our Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your personal information when You use the Application or the Website and tells You about Your privacy rights and how the law protects You. Please read Our Privacy Policy carefully before using Our Service. 54 | 55 | ### Links to Other Websites 56 | 57 | Our Service may contain links to third-party web sites or services that are not owned or controlled by the Company. 58 | 59 | The Company has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party web sites or services. You further acknowledge and agree that the Company shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such web sites or services. 60 | 61 | ### Termination 62 | 63 | We may terminate or suspend Your access immediately, without prior notice or liability, for any reason whatsoever, including without limitation if You breach these Terms and Conditions. 64 | 65 | Upon termination, Your right to use the Service will cease immediately. 66 | 67 | ### Limitation of Liability 68 | 69 | Notwithstanding any damages that You might incur, the entire liability of the Company and any of its suppliers under any provision of this Terms and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by You through the Service or 100 USD if You haven't purchased anything through the Service. 70 | 71 | To the maximum extent permitted by applicable law, in no event shall the Company or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the use of or inability to use the Service, third-party software and/or third-party hardware used with the Service, or otherwise in connection with any provision of this Terms), even if the Company or any supplier has been advised of the possibility of such damages and even if the remedy fails of its essential purpose. 72 | 73 | Some states do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages, which means that some of the above limitations may not apply. In these states, each party's liability will be limited to the greatest extent permitted by law. 74 | 75 | ### "AS IS" and "AS AVAILABLE" Disclaimer 76 | 77 | The Service is provided to You "AS IS" and "AS AVAILABLE" and with all faults and defects without warranty of any kind. To the maximum extent permitted under applicable law, the Company, on its own behalf and on behalf of its Affiliates and its and their respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise, with respect to the Service, including all implied warranties of merchantability, fitness for a particular purpose, title and non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without limitation to the foregoing, the Company provides no warranty or undertaking, and makes no representation of any kind that the Service will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications, systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors or defects can or will be corrected. 78 | 79 | Without limiting the foregoing, neither the Company nor any of the company's provider makes any representation or warranty of any kind, express or implied: (i) as to the operation or availability of the Service, or the information, content, and materials or products included thereon; (ii) that the Service will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or currency of any information or content provided through the Service; or (iv) that the Service, its servers, the content, or e-mails sent from or on behalf of the Company are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful components. 80 | 81 | Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law. 82 | 83 | ### Governing Law 84 | 85 | The laws of the Country, excluding its conflicts of law rules, shall govern this Terms and Your use of the Service. Your use of the Application may also be subject to other local, state, national, or international laws. 86 | 87 | ### Disputes Resolution 88 | 89 | If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the Company. 90 | 91 | ### For European Union (EU) Users 92 | 93 | If You are a European Union consumer, you will benefit from any mandatory provisions of the law of the country in which you are resident in. 94 | 95 | ### United States Legal Compliance 96 | 97 | You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that has been designated by the United States government as a "terrorist supporting" country, and (ii) You are not listed on any United States government list of prohibited or restricted parties. 98 | 99 | ## Severability and Waiver 100 | 101 | ### Severability 102 | 103 | If any provision of these Terms is held to be unenforceable or invalid, such provision will be changed and interpreted to accomplish the objectives of such provision to the greatest extent possible under applicable law and the remaining provisions will continue in full force and effect. 104 | 105 | ### Waiver 106 | 107 | Except as provided herein, the failure to exercise a right or to require performance of an obligation under these Terms shall not effect a party's ability to exercise such right or require such performance at any time thereafter nor shall the waiver of a breach constitute a waiver of any subsequent breach. 108 | 109 | ## Prohibited Uses 110 | 111 | ### Prohibition on Scraping Images and GIFs 112 | 113 | Scraping, extracting, or otherwise copying images, GIFs, or other visual media from **NEKOSBEST** using bots, crawlers, scripts, or similar technologies is strictly forbidden. Violations may result in an immediate IP ban from accessing **NEKOSBEST**. 114 | 115 | ### Prohibition on Use for AI Training and Related Purposes 116 | 117 | The use of any images, GIFs, or other visual media from **NEKOSBEST** for training artificial intelligence (AI), machine learning models, or any similar technologies is forbidden. We do not own the images or GIFs displayed on this website. They are shared for entertainment purposes with credits to their original sources. 118 | 119 | ### Translation Interpretation 120 | 121 | These Terms and Conditions may have been translated if We have made them available to You on our Service. You agree that the original English text shall prevail in the case of a dispute. 122 | 123 | ### Changes to These Terms and Conditions 124 | 125 | We reserve the right, at Our sole discretion, to modify or replace these Terms at any time. 126 | 127 | By continuing to access or use Our Service after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new terms, in whole or in part, please stop using the website and the Service. 128 | 129 | ## Contact Us 130 | 131 | If you have any questions about these Terms and Conditions, You can contact us: 132 | 133 | - By email: **[contact@nekos.best](mailto:contact@nekos.best)** 134 | -------------------------------------------------------------------------------- /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: 500; 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 | 497 | hr { 498 | color: #e91e63; 499 | } 500 | -------------------------------------------------------------------------------- /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 | {{#if favicon_svg}} 23 | 24 | {{/if}} 25 | {{#if favicon_png}} 26 | 27 | {{/if}} 28 | 29 | 30 | 31 | {{#if print_enable}} 32 | 33 | {{/if}} 34 | 35 | 36 | 37 | {{#if copy_fonts}} 38 | 39 | {{/if}} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{#each additional_css}} 48 | 49 | {{/each}} 50 | 51 | {{#if mathjax_support}} 52 | 53 | 54 | {{/if}} 55 | 56 | 57 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 | 84 | 85 | 86 | 95 | 96 | 97 | 98 | 99 | 112 | 113 | 123 | 124 |
125 | 126 |
127 | {{> header}} 128 | 129 | 173 | 174 | {{#if search_enabled}} 175 | 185 | {{/if}} 186 | 187 | 188 | 195 | 196 |
197 |
198 | {{{ content }}} 199 |
200 | 201 | 217 |
218 |
219 | 220 | 233 | 234 |
235 | 236 | {{#if live_reload_endpoint}} 237 | 238 | 253 | {{/if}} 254 | 255 | {{#if google_analytics}} 256 | 257 | 272 | {{/if}} 273 | 274 | {{#if playground_line_numbers}} 275 | 278 | {{/if}} 279 | 280 | {{#if playground_copyable}} 281 | 284 | {{/if}} 285 | 286 | {{#if playground_js}} 287 | 288 | 289 | 290 | 291 | 292 | {{/if}} 293 | 294 | {{#if search_js}} 295 | 296 | 297 | 298 | {{/if}} 299 | 300 | 301 | 302 | 303 | 304 | 305 | {{#each additional_js}} 306 | 307 | {{/each}} 308 | 309 | {{#if is_print}} 310 | {{#if mathjax_support}} 311 | 318 | {{else}} 319 | 324 | {{/if}} 325 | {{/if}} 326 | 327 |
328 | 329 | -------------------------------------------------------------------------------- /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 | 321 | if (theme == 'nekosbest' || theme == 'navy') { 322 | stylesheets.tomorrowNight.disabled = false; 323 | stylesheets.highlight.disabled = true; 324 | } 325 | 326 | setTimeout(function () { 327 | themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; 328 | }, 1); 329 | 330 | var previousTheme = get_theme(); 331 | 332 | if (store) { 333 | try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } 334 | } 335 | 336 | html.classList.remove(previousTheme); 337 | html.classList.add(theme); 338 | } 339 | 340 | // Set theme 341 | var theme = get_theme(); 342 | 343 | set_theme(theme, false); 344 | 345 | themeToggleButton.addEventListener('click', function () { 346 | if (themePopup.style.display === 'block') { 347 | hideThemes(); 348 | } else { 349 | showThemes(); 350 | } 351 | }); 352 | 353 | themePopup.addEventListener('click', function (e) { 354 | var theme; 355 | if (e.target.className === "theme") { 356 | theme = e.target.id; 357 | } else if (e.target.parentElement.className === "theme") { 358 | theme = e.target.parentElement.id; 359 | } else { 360 | return; 361 | } 362 | set_theme(theme); 363 | }); 364 | 365 | themePopup.addEventListener('focusout', function(e) { 366 | // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) 367 | if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { 368 | hideThemes(); 369 | } 370 | }); 371 | 372 | // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 373 | document.addEventListener('click', function(e) { 374 | if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { 375 | hideThemes(); 376 | } 377 | }); 378 | 379 | document.addEventListener('keydown', function (e) { 380 | if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } 381 | if (!themePopup.contains(e.target)) { return; } 382 | 383 | switch (e.key) { 384 | case 'Escape': 385 | e.preventDefault(); 386 | hideThemes(); 387 | break; 388 | case 'ArrowUp': 389 | e.preventDefault(); 390 | var li = document.activeElement.parentElement; 391 | if (li && li.previousElementSibling) { 392 | li.previousElementSibling.querySelector('button').focus(); 393 | } 394 | break; 395 | case 'ArrowDown': 396 | e.preventDefault(); 397 | var li = document.activeElement.parentElement; 398 | if (li && li.nextElementSibling) { 399 | li.nextElementSibling.querySelector('button').focus(); 400 | } 401 | break; 402 | case 'Home': 403 | e.preventDefault(); 404 | themePopup.querySelector('li:first-child button').focus(); 405 | break; 406 | case 'End': 407 | e.preventDefault(); 408 | themePopup.querySelector('li:last-child button').focus(); 409 | break; 410 | } 411 | }); 412 | })(); 413 | 414 | (function sidebar() { 415 | var html = document.querySelector("html"); 416 | var sidebar = document.getElementById("sidebar"); 417 | var sidebarLinks = document.querySelectorAll('#sidebar a'); 418 | var sidebarToggleButton = document.getElementById("sidebar-toggle"); 419 | var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); 420 | var firstContact = null; 421 | 422 | function showSidebar() { 423 | html.classList.remove('sidebar-hidden') 424 | html.classList.add('sidebar-visible'); 425 | Array.from(sidebarLinks).forEach(function (link) { 426 | link.setAttribute('tabIndex', 0); 427 | }); 428 | sidebarToggleButton.setAttribute('aria-expanded', true); 429 | sidebar.setAttribute('aria-hidden', false); 430 | try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } 431 | } 432 | 433 | 434 | var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); 435 | 436 | function toggleSection(ev) { 437 | ev.currentTarget.parentElement.classList.toggle('expanded'); 438 | } 439 | 440 | Array.from(sidebarAnchorToggles).forEach(function (el) { 441 | el.addEventListener('click', toggleSection); 442 | }); 443 | 444 | function hideSidebar() { 445 | html.classList.remove('sidebar-visible') 446 | html.classList.add('sidebar-hidden'); 447 | Array.from(sidebarLinks).forEach(function (link) { 448 | link.setAttribute('tabIndex', -1); 449 | }); 450 | sidebarToggleButton.setAttribute('aria-expanded', false); 451 | sidebar.setAttribute('aria-hidden', true); 452 | try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } 453 | } 454 | 455 | // Toggle sidebar 456 | sidebarToggleButton.addEventListener('click', function sidebarToggle() { 457 | if (html.classList.contains("sidebar-hidden")) { 458 | var current_width = parseInt( 459 | document.documentElement.style.getPropertyValue('--sidebar-width'), 10); 460 | if (current_width < 150) { 461 | document.documentElement.style.setProperty('--sidebar-width', '150px'); 462 | } 463 | showSidebar(); 464 | } else if (html.classList.contains("sidebar-visible")) { 465 | hideSidebar(); 466 | } else { 467 | if (getComputedStyle(sidebar)['transform'] === 'none') { 468 | hideSidebar(); 469 | } else { 470 | showSidebar(); 471 | } 472 | } 473 | }); 474 | 475 | sidebarResizeHandle.addEventListener('mousedown', initResize, false); 476 | 477 | function initResize(e) { 478 | window.addEventListener('mousemove', resize, false); 479 | window.addEventListener('mouseup', stopResize, false); 480 | html.classList.add('sidebar-resizing'); 481 | } 482 | function resize(e) { 483 | var pos = (e.clientX - sidebar.offsetLeft); 484 | if (pos < 20) { 485 | hideSidebar(); 486 | } else { 487 | if (html.classList.contains("sidebar-hidden")) { 488 | showSidebar(); 489 | } 490 | pos = Math.min(pos, window.innerWidth - 100); 491 | document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); 492 | } 493 | } 494 | //on mouseup remove windows functions mousemove & mouseup 495 | function stopResize(e) { 496 | html.classList.remove('sidebar-resizing'); 497 | window.removeEventListener('mousemove', resize, false); 498 | window.removeEventListener('mouseup', stopResize, false); 499 | } 500 | 501 | document.addEventListener('touchstart', function (e) { 502 | firstContact = { 503 | x: e.touches[0].clientX, 504 | time: Date.now() 505 | }; 506 | }, { passive: true }); 507 | 508 | document.addEventListener('touchmove', function (e) { 509 | if (!firstContact) 510 | return; 511 | 512 | var curX = e.touches[0].clientX; 513 | var xDiff = curX - firstContact.x, 514 | tDiff = Date.now() - firstContact.time; 515 | 516 | if (tDiff < 250 && Math.abs(xDiff) >= 150) { 517 | if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) 518 | showSidebar(); 519 | else if (xDiff < 0 && curX < 300) 520 | hideSidebar(); 521 | 522 | firstContact = null; 523 | } 524 | }, { passive: true }); 525 | 526 | // Scroll sidebar to current active section 527 | var activeSection = document.getElementById("sidebar").querySelector(".active"); 528 | if (activeSection) { 529 | // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView 530 | activeSection.scrollIntoView({ block: 'center' }); 531 | } 532 | })(); 533 | 534 | (function chapterNavigation() { 535 | document.addEventListener('keydown', function (e) { 536 | if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } 537 | if (window.search && window.search.hasFocus()) { return; } 538 | 539 | switch (e.key) { 540 | case 'ArrowRight': 541 | e.preventDefault(); 542 | var nextButton = document.querySelector('.nav-chapters.next'); 543 | if (nextButton) { 544 | window.location.href = nextButton.href; 545 | } 546 | break; 547 | case 'ArrowLeft': 548 | e.preventDefault(); 549 | var previousButton = document.querySelector('.nav-chapters.previous'); 550 | if (previousButton) { 551 | window.location.href = previousButton.href; 552 | } 553 | break; 554 | } 555 | }); 556 | })(); 557 | 558 | (function clipboard() { 559 | var clipButtons = document.querySelectorAll('.clip-button'); 560 | 561 | function hideTooltip(elem) { 562 | elem.firstChild.innerText = ""; 563 | elem.className = 'fa fa-copy clip-button'; 564 | } 565 | 566 | function showTooltip(elem, msg) { 567 | elem.firstChild.innerText = msg; 568 | elem.className = 'fa fa-copy tooltipped'; 569 | } 570 | 571 | var clipboardSnippets = new ClipboardJS('.clip-button', { 572 | text: function (trigger) { 573 | hideTooltip(trigger); 574 | let playground = trigger.closest("pre"); 575 | return playground_text(playground); 576 | } 577 | }); 578 | 579 | Array.from(clipButtons).forEach(function (clipButton) { 580 | clipButton.addEventListener('mouseout', function (e) { 581 | hideTooltip(e.currentTarget); 582 | }); 583 | }); 584 | 585 | clipboardSnippets.on('success', function (e) { 586 | e.clearSelection(); 587 | showTooltip(e.trigger, "Copied!"); 588 | }); 589 | 590 | clipboardSnippets.on('error', function (e) { 591 | showTooltip(e.trigger, "Clipboard error!"); 592 | }); 593 | })(); 594 | 595 | (function scrollToTop () { 596 | var menuTitle = document.querySelector('.menu-title'); 597 | 598 | menuTitle.addEventListener('click', function () { 599 | document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); 600 | }); 601 | })(); 602 | 603 | (function controllMenu() { 604 | var menu = document.getElementById('menu-bar'); 605 | 606 | (function controllPosition() { 607 | var scrollTop = document.scrollingElement.scrollTop; 608 | var prevScrollTop = scrollTop; 609 | var minMenuY = -menu.clientHeight - 50; 610 | // When the script loads, the page can be at any scroll (e.g. if you reforesh it). 611 | menu.style.top = scrollTop + 'px'; 612 | // Same as parseInt(menu.style.top.slice(0, -2), but faster 613 | var topCache = menu.style.top.slice(0, -2); 614 | menu.classList.remove('sticky'); 615 | var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster 616 | document.addEventListener('scroll', function () { 617 | scrollTop = Math.max(document.scrollingElement.scrollTop, 0); 618 | // `null` means that it doesn't need to be updated 619 | var nextSticky = null; 620 | var nextTop = null; 621 | var scrollDown = scrollTop > prevScrollTop; 622 | var menuPosAbsoluteY = topCache - scrollTop; 623 | if (scrollDown) { 624 | nextSticky = false; 625 | if (menuPosAbsoluteY > 0) { 626 | nextTop = prevScrollTop; 627 | } 628 | } else { 629 | if (menuPosAbsoluteY > 0) { 630 | nextSticky = true; 631 | } else if (menuPosAbsoluteY < minMenuY) { 632 | nextTop = prevScrollTop + minMenuY; 633 | } 634 | } 635 | if (nextSticky === true && stickyCache === false) { 636 | menu.classList.add('sticky'); 637 | stickyCache = true; 638 | } else if (nextSticky === false && stickyCache === true) { 639 | menu.classList.remove('sticky'); 640 | stickyCache = false; 641 | } 642 | if (nextTop !== null) { 643 | menu.style.top = nextTop + 'px'; 644 | topCache = nextTop; 645 | } 646 | prevScrollTop = scrollTop; 647 | }, { passive: true }); 648 | })(); 649 | (function controllBorder() { 650 | menu.classList.remove('bordered'); 651 | document.addEventListener('scroll', function () { 652 | if (menu.offsetTop === 0) { 653 | menu.classList.remove('bordered'); 654 | } else { 655 | menu.classList.add('bordered'); 656 | } 657 | }, { passive: true }); 658 | })(); 659 | })(); 660 | --------------------------------------------------------------------------------