├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── README.md ├── SUMMARY.md ├── config.toml ├── content ├── _index.md ├── authors │ └── _index.md ├── blog │ └── _index.md └── docs │ ├── _index.md │ ├── back-matter │ ├── 99-1-references.md │ ├── 99-9-license.md │ └── _index.md │ ├── front-matter │ ├── 00-1-authors.md │ ├── 00-2-preface.md │ └── _index.md │ ├── images │ ├── swagger.png │ ├── trace-details.png │ └── trace-events.png │ └── part-1 │ ├── 01-introduction.md │ ├── 02-hello-world.md │ ├── 03-useful-things.md │ ├── 04-application-structure.md │ ├── 05-tokio-hyper-axum.md │ ├── 06-persistence.md │ ├── 07-api-design.md │ ├── 08-resiliency.md │ ├── 09-observability.md │ ├── 10-testing.md │ ├── 11-deployment.md │ └── _index.md ├── sass ├── _custom.scss ├── bootstrap │ ├── LICENSE │ ├── README.md │ ├── dist │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-grid.rtl.css │ │ │ ├── bootstrap-grid.rtl.css.map │ │ │ ├── bootstrap-grid.rtl.min.css │ │ │ ├── bootstrap-grid.rtl.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap-reboot.rtl.css │ │ │ ├── bootstrap-reboot.rtl.css.map │ │ │ ├── bootstrap-reboot.rtl.min.css │ │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ │ ├── bootstrap-utilities.css │ │ │ ├── bootstrap-utilities.css.map │ │ │ ├── bootstrap-utilities.min.css │ │ │ ├── bootstrap-utilities.min.css.map │ │ │ ├── bootstrap-utilities.rtl.css │ │ │ ├── bootstrap-utilities.rtl.css.map │ │ │ ├── bootstrap-utilities.rtl.min.css │ │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ ├── bootstrap.min.css.map │ │ │ ├── bootstrap.rtl.css │ │ │ ├── bootstrap.rtl.css.map │ │ │ ├── bootstrap.rtl.min.css │ │ │ └── bootstrap.rtl.min.css.map │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.esm.js │ │ │ ├── bootstrap.esm.js.map │ │ │ ├── bootstrap.esm.min.js │ │ │ ├── bootstrap.esm.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ ├── js │ │ ├── dist │ │ │ ├── alert.js │ │ │ ├── alert.js.map │ │ │ ├── base-component.js │ │ │ ├── base-component.js.map │ │ │ ├── button.js │ │ │ ├── button.js.map │ │ │ ├── carousel.js │ │ │ ├── carousel.js.map │ │ │ ├── collapse.js │ │ │ ├── collapse.js.map │ │ │ ├── dom │ │ │ │ ├── data.js │ │ │ │ ├── data.js.map │ │ │ │ ├── event-handler.js │ │ │ │ ├── event-handler.js.map │ │ │ │ ├── manipulator.js │ │ │ │ ├── manipulator.js.map │ │ │ │ ├── selector-engine.js │ │ │ │ └── selector-engine.js.map │ │ │ ├── dropdown.js │ │ │ ├── dropdown.js.map │ │ │ ├── modal.js │ │ │ ├── modal.js.map │ │ │ ├── offcanvas.js │ │ │ ├── offcanvas.js.map │ │ │ ├── popover.js │ │ │ ├── popover.js.map │ │ │ ├── scrollspy.js │ │ │ ├── scrollspy.js.map │ │ │ ├── tab.js │ │ │ ├── tab.js.map │ │ │ ├── toast.js │ │ │ ├── toast.js.map │ │ │ ├── tooltip.js │ │ │ └── tooltip.js.map │ │ └── src │ │ │ ├── alert.js │ │ │ ├── base-component.js │ │ │ ├── button.js │ │ │ ├── carousel.js │ │ │ ├── collapse.js │ │ │ ├── dom │ │ │ ├── data.js │ │ │ ├── event-handler.js │ │ │ ├── manipulator.js │ │ │ └── selector-engine.js │ │ │ ├── dropdown.js │ │ │ ├── modal.js │ │ │ ├── offcanvas.js │ │ │ ├── popover.js │ │ │ ├── scrollspy.js │ │ │ ├── tab.js │ │ │ ├── toast.js │ │ │ ├── tooltip.js │ │ │ └── util │ │ │ ├── index.js │ │ │ ├── sanitizer.js │ │ │ └── scrollbar.js │ ├── package.json │ └── scss │ │ ├── _accordion.scss │ │ ├── _alert.scss │ │ ├── _badge.scss │ │ ├── _breadcrumb.scss │ │ ├── _button-group.scss │ │ ├── _buttons.scss │ │ ├── _card.scss │ │ ├── _carousel.scss │ │ ├── _close.scss │ │ ├── _containers.scss │ │ ├── _dropdown.scss │ │ ├── _forms.scss │ │ ├── _functions.scss │ │ ├── _grid.scss │ │ ├── _helpers.scss │ │ ├── _images.scss │ │ ├── _list-group.scss │ │ ├── _mixins.scss │ │ ├── _modal.scss │ │ ├── _nav.scss │ │ ├── _navbar.scss │ │ ├── _offcanvas.scss │ │ ├── _pagination.scss │ │ ├── _popover.scss │ │ ├── _progress.scss │ │ ├── _reboot.scss │ │ ├── _root.scss │ │ ├── _spinners.scss │ │ ├── _tables.scss │ │ ├── _toasts.scss │ │ ├── _tooltip.scss │ │ ├── _transitions.scss │ │ ├── _type.scss │ │ ├── _utilities.scss │ │ ├── _variables.scss │ │ ├── bootstrap-grid.scss │ │ ├── bootstrap-reboot.scss │ │ ├── bootstrap-utilities.scss │ │ ├── bootstrap.scss │ │ ├── forms │ │ ├── _floating-labels.scss │ │ ├── _form-check.scss │ │ ├── _form-control.scss │ │ ├── _form-range.scss │ │ ├── _form-select.scss │ │ ├── _form-text.scss │ │ ├── _input-group.scss │ │ ├── _labels.scss │ │ └── _validation.scss │ │ ├── helpers │ │ ├── _clearfix.scss │ │ ├── _colored-links.scss │ │ ├── _position.scss │ │ ├── _ratio.scss │ │ ├── _stretched-link.scss │ │ ├── _text-truncation.scss │ │ └── _visually-hidden.scss │ │ ├── mixins │ │ ├── _alert.scss │ │ ├── _border-radius.scss │ │ ├── _box-shadow.scss │ │ ├── _breakpoints.scss │ │ ├── _buttons.scss │ │ ├── _caret.scss │ │ ├── _clearfix.scss │ │ ├── _container.scss │ │ ├── _deprecate.scss │ │ ├── _forms.scss │ │ ├── _gradients.scss │ │ ├── _grid.scss │ │ ├── _image.scss │ │ ├── _list-group.scss │ │ ├── _lists.scss │ │ ├── _pagination.scss │ │ ├── _reset-text.scss │ │ ├── _resize.scss │ │ ├── _table-variants.scss │ │ ├── _text-truncate.scss │ │ ├── _transition.scss │ │ ├── _utilities.scss │ │ └── _visually-hidden.scss │ │ ├── utilities │ │ └── _api.scss │ │ └── vendor │ │ └── _rfs.scss ├── common │ ├── _dark.scss │ ├── _fonts.scss │ ├── _global.scss │ └── _variables.scss ├── components │ ├── _alerts.scss │ ├── _buttons.scss │ ├── _code.scss │ ├── _comments.scss │ ├── _doks.scss │ ├── _footnote.scss │ ├── _forms.scss │ ├── _images.scss │ ├── _search.scss │ ├── _syntax.scss │ └── _tables.scss ├── layouts │ ├── _footer.scss │ ├── _header.scss │ ├── _pages.scss │ ├── _posts.scss │ └── _sidebar.scss └── main.scss ├── scripts └── mdbook.sh ├── static ├── _headers ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── doks.png ├── doks.svg ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── fonts │ └── vendor │ │ └── jost │ │ ├── jost-v4-latin-500.woff │ │ ├── jost-v4-latin-500.woff2 │ │ ├── jost-v4-latin-500italic.woff │ │ ├── jost-v4-latin-500italic.woff2 │ │ ├── jost-v4-latin-700.woff │ │ ├── jost-v4-latin-700.woff2 │ │ ├── jost-v4-latin-700italic.woff │ │ ├── jost-v4-latin-700italic.woff2 │ │ ├── jost-v4-latin-italic.woff │ │ ├── jost-v4-latin-italic.woff2 │ │ ├── jost-v4-latin-regular.woff │ │ └── jost-v4-latin-regular.woff2 ├── index.js ├── js │ ├── main.js │ └── search.js ├── logo-doks.png ├── plugins │ └── elasticlunr.min.js ├── processed_images │ ├── swagger.a17a5d37fc14a3f9.png │ ├── trace-details.860ba2e9b5d2d0c3.png │ └── trace-events.3d87eee666799877.png └── site.webmanifest ├── templates ├── 404.html ├── authors │ ├── list.html │ └── single.html ├── base.html ├── blog │ ├── page.html │ └── section.html ├── docs │ ├── page.html │ └── section.html ├── index.html ├── macros │ ├── docs-edit-page.html │ ├── docs-navigation.html │ ├── docs-sidebar.html │ ├── docs-toc.html │ ├── footer.html │ ├── head.html │ ├── header.html │ ├── javascript.html │ ├── math.html │ ├── page-publish-metadata.html │ └── section-navigation.html ├── page.html ├── robots.txt ├── section.html └── shortcodes │ └── resize_image.html └── theme.toml /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v2 14 | 15 | - name: Set timezone 16 | uses: szenius/set-timezone@v1.0 17 | with: 18 | timezoneLinux: "Europe/Budapest" 19 | 20 | - name: Check date and time 21 | run: date 22 | 23 | - name: Run zola build 24 | run: docker run -v ${{ github.workspace }}:/app ghcr.io/sapati/zola:v0.17.1-hu -r app build 25 | 26 | - name: Deploy to Cloudflare Workers with Wrangler 27 | uses: cloudflare/wrangler-action@2.0.0 28 | with: 29 | apiToken: ${{ secrets.CLOUDFLARE_TOKEN }} 30 | accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 31 | command: pages publish public --project-name rust-api-dev 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | .vs 3 | .vscode 4 | .idea 5 | mdbook 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Source repository for rust-api.dev 3 | 4 | Based on Zola and the Adidoks theme 5 | 6 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | 2 | # API development with RUST 3 | 4 | - [Authors](00-1-authors.md) 5 | - [Preface](00-2-preface.md) 6 | 7 | # Part 1 8 | 9 | - [Introduction](01-introduction.md) 10 | - [Hello World!](02-hello-world.md) 11 | - [Useful things](03-useful-things.md) 12 | - [Application structure](04-application-structure.md) 13 | - [The Axum stack](05-tokio-hyper-axum.md) 14 | - [Persistence](06-persistence.md) 15 | - [API design](07-api-design.md) 16 | - [Resiliency](08-resiliency.md) 17 | - [Observability](09-observability.md) 18 | - [Testing](10-testing.md) 19 | - [Deployment](11-deployment.md) 20 | 21 | # Back matter 22 | 23 | - [References](99-1-references.md) 24 | - [License](99-9-license.md) 25 | 26 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | # The URL the site will be built for 2 | base_url = "https://rust-api.dev/" 3 | title = "rust-api.dev" 4 | description = "API development with Rust" 5 | 6 | # The default language; used in feeds and search index 7 | # Note: the search index doesn't support Chinese/Japanese/Korean Languages 8 | default_language = "en" 9 | 10 | # Whether to automatically compile all Sass files in the sass directory 11 | compile_sass = true 12 | 13 | # Whether to generate a feed file for the site 14 | generate_feed = true 15 | 16 | # When set to "true", the generated HTML files are minified. 17 | minify_html = false 18 | 19 | # The taxonomies to be rendered for the site and their configuration. 20 | taxonomies = [ 21 | {name = "authors"}, # Basic definition: no feed or pagination 22 | ] 23 | 24 | # Whether to build a search index to be used later on by a JavaScript library 25 | # When set to "true", a search index is built from the pages and section 26 | # content for `default_language`. 27 | build_search_index = true 28 | 29 | [search] 30 | # Whether to include the title of the page/section in the index 31 | include_title = true 32 | # Whether to include the description of the page/section in the index 33 | include_description = false 34 | # Whether to include the rendered content of the page/section in the index 35 | include_content = true 36 | 37 | [markdown] 38 | # Whether to do syntax highlighting. 39 | # Theme can be customised by setting the `highlight_theme` 40 | # variable to a theme supported by Zola 41 | highlight_code = true 42 | 43 | [extra] 44 | # Put all your custom variables here 45 | # author = "Aaran Xu" 46 | github = "https://github.com/Rust-Book-Collective/rust-api-dev" 47 | # twitter = "https://twitter.com/aaranxu" 48 | # email = "aaranxu@outlook.com" 49 | ganalytics = "G-T55T07FSJJ" # Google Analytics code 50 | 51 | # If running on netlify.app site, set to true 52 | is_netlify = false 53 | 54 | # Set HTML file language 55 | language_code = "en-US" 56 | 57 | # Set theme-color meta tag for Chrome browser 58 | theme_color = "#fff" 59 | 60 | # More about site's title 61 | title_separator = "|" # set as |, -, _, etc 62 | title_addition = "Modern Documentation Theme" 63 | 64 | 65 | # Set date format in blog publish metadata 66 | timeformat = "%Y-%m-%d %H:%M:%S" # e.g. 2021-05-01 09:19:42 67 | timezone = "Europe/Budapest" 68 | 69 | # Edit page on reposity or not 70 | edit_page = false 71 | # docs_repo = "https://github.com/aaranxu/adidoks" 72 | repo_branch = "main" 73 | 74 | ## Math settings 75 | # options: true, false. Enable math support globally, 76 | # default: false. You can always enable math on a per page. 77 | math = false 78 | library = "katex" # options: "katex", "mathjax". default is "katex". 79 | 80 | ## Open Graph + Twitter Cards 81 | [extra.open] 82 | enable = true 83 | # this image will be used as fallback if a page has no image of its own 84 | image = "doks.png" 85 | twitter_site = "" 86 | twitter_creator = "" 87 | facebook_author = "" 88 | facebook_publisher = "" 89 | og_locale = "en_US" 90 | 91 | ## JSON-LD 92 | [extra.schema] 93 | type = "Organization" 94 | logo = "logo-doks.png" 95 | # twitter = "https://twitter.com/aaranxu" 96 | linked_in = "" 97 | github = "https://github.com/Rust-Book-Collective/rust-api-dev" 98 | section = "blog" # see config.extra.main~url 99 | ## Sitelinks Search Box 100 | site_links_search_box = false 101 | 102 | [[extra.menu.social]] 103 | name = "GitHub" 104 | pre = '' 105 | url = "https://github.com/Rust-Book-Collective/rust-api-dev" 106 | post = "v0.1.0" 107 | weight = 20 108 | 109 | # Footer contents 110 | [extra.footer] 111 | info = 'Powered by Zola and AdiDoks' 112 | 113 | -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "API development with Rust" 3 | 4 | 5 | # The homepage contents 6 | [extra] 7 | lead = 'API development with Rust' 8 | url = "/docs/front-matter/preface" 9 | url_button = "Get started" 10 | repo_url = "https://github.com/" 11 | repo_license = "CC-BY-SA 4.0" 12 | 13 | # Menu items 14 | [[extra.menu.main]] 15 | name = "Docs" 16 | section = "docs" 17 | url = "/docs/front-matter/preface" 18 | weight = 10 19 | 20 | +++ 21 | -------------------------------------------------------------------------------- /content/authors/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Authors" 3 | description = "The authurs of the blog articles." 4 | date = 2021-04-01T08:00:00+00:00 5 | updated = 2021-04-01T08:00:00+00:00 6 | draft = false 7 | 8 | # [extra.author_pages] 9 | # "aaran-xu" = "authors/aaran-xu.md" 10 | +++ 11 | 12 | The authors of the blog articles. 13 | 14 | -------------------------------------------------------------------------------- /content/blog/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Blog" 3 | description = "Blog" 4 | sort_by = "date" 5 | paginate_by = 2 6 | template = "blog/section.html" 7 | +++ 8 | -------------------------------------------------------------------------------- /content/docs/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Docs" 3 | description = "The documents of the AdiDoks theme." 4 | date = 2025-05-01T08:00:00+00:00 5 | updated = 2021-05-01T08:00:00+00:00 6 | sort_by = "weight" 7 | weight = 1 8 | template = "docs/section.html" 9 | +++ 10 | -------------------------------------------------------------------------------- /content/docs/back-matter/99-1-references.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "References" 3 | description = "" 4 | date = 2021-05-01T18:20:00+00:00 5 | updated = 2021-05-01T18:20:00+00:00 6 | draft = false 7 | weight = 1991 8 | sort_by = "weight" 9 | template = "docs/page.html" 10 | slug = "references" 11 | 12 | [extra] 13 | lead = "" 14 | toc = true 15 | top = false 16 | +++ 17 | 18 | 19 | - Architectural Styles and the Design of Network-based Software Architectures\ 20 | [https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm](https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm) 21 | 22 | - REST\ 23 | [https://en.wikipedia.org/wiki/REST](https://en.wikipedia.org/wiki/REST) 24 | 25 | - Richardson Maturity Model\ 26 | [https://en.wikipedia.org/wiki/Richardson_Maturity_Model](https://en.wikipedia.org/wiki/Richardson_Maturity_Model) 27 | 28 | - The Rust Programming Language\ 29 | [https://doc.rust-lang.org/book/](https://doc.rust-lang.org/book/) 30 | 31 | - Asynchronous Programming in Rust\ 32 | [https://rust-lang.github.io/async-book/](https://rust-lang.github.io/async-book/) 33 | 34 | - An overview of HTTP\ 35 | [https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview](https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview) 36 | 37 | - The Rustonimicon\ 38 | [https://doc.rust-lang.org/nomicon/](https://doc.rust-lang.org/nomicon/) 39 | 40 | - RESTful Web API Patterns & Practices Cookbook\ 41 | [https://www.oreilly.com/library/view/restful-web-api/9781098106737/](https://www.oreilly.com/library/view/restful-web-api/9781098106737/) 42 | 43 | 44 | -------------------------------------------------------------------------------- /content/docs/back-matter/99-9-license.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "License" 3 | description = "" 4 | date = 2021-05-01T18:20:00+00:00 5 | updated = 2021-05-01T18:20:00+00:00 6 | draft = false 7 | weight = 1999 8 | sort_by = "weight" 9 | template = "docs/page.html" 10 | slug = "license" 11 | 12 | [extra] 13 | lead = "" 14 | toc = true 15 | top = false 16 | +++ 17 | 18 | The [Adidoks](https://github.com/aaranxu/adidoks) theme is licensed under the 19 | [MIT](https://github.com/aaranxu/adidoks/blob/main/LICENSE) license. 20 | 21 | The sample codes are in their own repository on 22 | [GitHub](https://github.com/Rust-Book-Collective/rust-api-code), licensed under the 23 | [MIT](https://github.com/Rust-Book-Collective/rust-api-code/blob/main/LICENSE) license. 24 | 25 | The content of this site licensed under [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) 26 | 27 | ## You are free to: 28 | 29 | - Share — copy and redistribute the material in any medium or format for any purpose, even commercially. 30 | - Adapt — remix, transform, and build upon the material for any purpose, even commercially. 31 | - The licensor cannot revoke these freedoms as long as you follow the license terms. 32 | 33 | ## Under the following terms: 34 | 35 | - Attribution — You must give appropriate credit , provide a link to the license, and indicate if changes were made . You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 36 | - ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. 37 | 38 | - No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. 39 | 40 | ## Notices: 41 | 42 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation . 43 | 44 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. 45 | 46 | 47 | -------------------------------------------------------------------------------- /content/docs/back-matter/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Back matter" 3 | description = "Chapters" 4 | date = 2025-05-01T08:00:00+00:00 5 | updated = 2021-05-01T08:00:00+00:00 6 | sort_by = "weight" 7 | weight = 1990 8 | template = "docs/section.html" 9 | +++ 10 | -------------------------------------------------------------------------------- /content/docs/front-matter/00-1-authors.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "About the Authors" 3 | description = "" 4 | date = 2021-05-01T18:20:00+00:00 5 | updated = 2021-05-01T18:20:00+00:00 6 | draft = false 7 | weight = 1001 8 | sort_by = "weight" 9 | template = "docs/page.html" 10 | slug = "authors" 11 | 12 | [extra] 13 | lead = "" 14 | toc = true 15 | top = false 16 | +++ 17 | 18 | **[József Dubravszky](https://www.linkedin.com/in/djozsef/)**: CTO of [Mito Digital Zrt.](https://mito.hu/), 19 | Hungary. 20 | 21 | **[Nikola Kovács](https://github.com/nkovacs)**: Lead Backend Developer at 22 | [Mito Digital Zrt.](https://mito.hu/), Hungary. 23 | 24 | **[Sándor Apáti](https://apatisandor.hu/authors/sanyi/)**: Architect at [Mito Digital Zrt.](https://mito.hu/), 25 | Hungary. 26 | 25 years of experience in backend development and systems engineering. MSc 27 | in Computer Science and Engineering. Certified AWS Solution Architect 28 | Professional and Advanced Networking Specialist. 29 | -------------------------------------------------------------------------------- /content/docs/front-matter/00-2-preface.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Preface" 3 | description = "" 4 | date = 2021-05-01T18:20:00+00:00 5 | updated = 2021-05-01T18:20:00+00:00 6 | draft = false 7 | weight = 1002 8 | sort_by = "weight" 9 | template = "docs/page.html" 10 | slug = "preface" 11 | 12 | [extra] 13 | lead = "" 14 | toc = true 15 | top = false 16 | +++ 17 | 18 | If you you are Rust a developer, have a basic knowledge of the language 19 | and want to expand your knowledge to build resilient REST API services, 20 | this book is for you. Even if you have experience with API development, 21 | you may find useful ideas here. The book can be valuable for experienced 22 | API developers too, who consider switching to Rust from other languages. 23 | 24 | You need to have a basic knowledge of the Rust language, the compiler, 25 | the crate ecosystem. Read the official Rust book if you have not read it yet! 26 | You do not have to be familiar with async programming, we will explain that shortly. 27 | 28 | You will learn the basics of REST APIs, followed by the ecosystem and 29 | usual system architecture around a typical REST API. We will explain 30 | how to make your API secure, scalable and observable, so you can move 31 | it to production confidently. We will also show you how to implement 32 | automated testing, continuous integration and delivery. We will provide 33 | examples for cloud deployment scenarios too. 34 | 35 | We try to give you a solid theoretical foundation first, explaining 36 | what makes a good API and what makes it resilient. Then show you how 37 | to achieve those goals step-by-step, fulfilling one requirement at a time. 38 | 39 | Readers will learn how to build a simple asynchronous REST API server 40 | based on axum and tokio.rs. Then we will add persistence to SQL and 41 | NoSQL databases, JWT and OAuth authentication, authorization, tracing 42 | to log files or through OpenTelemetry, collecting metrics, implementing 43 | caching, request throttling. 44 | 45 | By the end of the book you will be able to build solid, well designed 46 | REST API services and deploy it on your own to cloud providers. 47 | 48 | -------------------------------------------------------------------------------- /content/docs/front-matter/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Front matter" 3 | description = "Chapters" 4 | date = 2025-05-01T08:00:00+00:00 5 | updated = 2021-05-01T08:00:00+00:00 6 | sort_by = "weight" 7 | weight = 1000 8 | template = "docs/section.html" 9 | +++ 10 | -------------------------------------------------------------------------------- /content/docs/images/swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Book-Collective/rust-api-dev/04ec0c487ef9ab7723c7be66c48ca75e4d4bbdcf/content/docs/images/swagger.png -------------------------------------------------------------------------------- /content/docs/images/trace-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Book-Collective/rust-api-dev/04ec0c487ef9ab7723c7be66c48ca75e4d4bbdcf/content/docs/images/trace-details.png -------------------------------------------------------------------------------- /content/docs/images/trace-events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Book-Collective/rust-api-dev/04ec0c487ef9ab7723c7be66c48ca75e4d4bbdcf/content/docs/images/trace-events.png -------------------------------------------------------------------------------- /content/docs/part-1/10-testing.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Testing" 3 | description = "" 4 | date = 2021-05-01T18:20:00+00:00 5 | updated = 2021-05-01T18:20:00+00:00 6 | draft = false 7 | weight = 1100 8 | sort_by = "weight" 9 | template = "docs/page.html" 10 | slug = "testing" 11 | 12 | [extra] 13 | lead = "" 14 | toc = true 15 | top = false 16 | +++ 17 | 18 | TBD 19 | 20 | - Unit testing 21 | - Integration testing 22 | - Mock data generation 23 | - Load testing 24 | 25 | -------------------------------------------------------------------------------- /content/docs/part-1/11-deployment.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Deployment" 3 | description = "" 4 | date = 2021-05-01T18:20:00+00:00 5 | updated = 2021-05-01T18:20:00+00:00 6 | draft = false 7 | weight = 1110 8 | sort_by = "weight" 9 | template = "docs/page.html" 10 | slug = "deployment" 11 | 12 | [extra] 13 | lead = "" 14 | toc = true 15 | top = false 16 | +++ 17 | 18 | TBD 19 | 20 | - CI / CD 21 | - containers 22 | - configuration 23 | 24 | -------------------------------------------------------------------------------- /content/docs/part-1/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = "Part 1" 3 | description = "Chapters" 4 | date = 2025-05-01T08:00:00+00:00 5 | updated = 2021-05-01T08:00:00+00:00 6 | sort_by = "weight" 7 | weight = 1010 8 | template = "docs/section.html" 9 | slug = "part-1" 10 | +++ 11 | -------------------------------------------------------------------------------- /sass/_custom.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rust-Book-Collective/rust-api-dev/04ec0c487ef9ab7723c7be66c48ca75e4d4bbdcf/sass/_custom.scss -------------------------------------------------------------------------------- /sass/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Twitter, Inc. 4 | Copyright (c) 2011-2021 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/base-component.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap base-component.js v5.0.0-beta3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js')) : 8 | typeof define === 'function' && define.amd ? define(['./dom/data'], factory) : 9 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Base = factory(global.Data)); 10 | }(this, (function (Data) { 'use strict'; 11 | 12 | function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } 13 | 14 | var Data__default = /*#__PURE__*/_interopDefaultLegacy(Data); 15 | 16 | /** 17 | * -------------------------------------------------------------------------- 18 | * Bootstrap (v5.0.0-beta3): base-component.js 19 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 20 | * -------------------------------------------------------------------------- 21 | */ 22 | /** 23 | * ------------------------------------------------------------------------ 24 | * Constants 25 | * ------------------------------------------------------------------------ 26 | */ 27 | 28 | const VERSION = '5.0.0-beta3'; 29 | 30 | class BaseComponent { 31 | constructor(element) { 32 | element = typeof element === 'string' ? document.querySelector(element) : element; 33 | 34 | if (!element) { 35 | return; 36 | } 37 | 38 | this._element = element; 39 | Data__default['default'].set(this._element, this.constructor.DATA_KEY, this); 40 | } 41 | 42 | dispose() { 43 | Data__default['default'].remove(this._element, this.constructor.DATA_KEY); 44 | this._element = null; 45 | } 46 | /** Static */ 47 | 48 | 49 | static getInstance(element) { 50 | return Data__default['default'].get(element, this.DATA_KEY); 51 | } 52 | 53 | static get VERSION() { 54 | return VERSION; 55 | } 56 | 57 | } 58 | 59 | return BaseComponent; 60 | 61 | }))); 62 | //# sourceMappingURL=base-component.js.map 63 | -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/base-component.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"base-component.js","sources":["../src/base-component.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.0.0-beta3): base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst VERSION = '5.0.0-beta3'\n\nclass BaseComponent {\n constructor(element) {\n element = typeof element === 'string' ? document.querySelector(element) : element\n\n if (!element) {\n return\n }\n\n this._element = element\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n this._element = null\n }\n\n /** Static */\n\n static getInstance(element) {\n return Data.get(element, this.DATA_KEY)\n }\n\n static get VERSION() {\n return VERSION\n }\n}\n\nexport default BaseComponent\n"],"names":["VERSION","BaseComponent","constructor","element","document","querySelector","_element","Data","set","DATA_KEY","dispose","remove","getInstance","get"],"mappings":";;;;;;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;EAIA;EACA;EACA;EACA;EACA;;EAEA,MAAMA,OAAO,GAAG,aAAhB;;EAEA,MAAMC,aAAN,CAAoB;EAClBC,EAAAA,WAAW,CAACC,OAAD,EAAU;EACnBA,IAAAA,OAAO,GAAG,OAAOA,OAAP,KAAmB,QAAnB,GAA8BC,QAAQ,CAACC,aAAT,CAAuBF,OAAvB,CAA9B,GAAgEA,OAA1E;;EAEA,QAAI,CAACA,OAAL,EAAc;EACZ;EACD;;EAED,SAAKG,QAAL,GAAgBH,OAAhB;EACAI,IAAAA,wBAAI,CAACC,GAAL,CAAS,KAAKF,QAAd,EAAwB,KAAKJ,WAAL,CAAiBO,QAAzC,EAAmD,IAAnD;EACD;;EAEDC,EAAAA,OAAO,GAAG;EACRH,IAAAA,wBAAI,CAACI,MAAL,CAAY,KAAKL,QAAjB,EAA2B,KAAKJ,WAAL,CAAiBO,QAA5C;EACA,SAAKH,QAAL,GAAgB,IAAhB;EACD;EAED;;;EAEkB,SAAXM,WAAW,CAACT,OAAD,EAAU;EAC1B,WAAOI,wBAAI,CAACM,GAAL,CAASV,OAAT,EAAkB,KAAKM,QAAvB,CAAP;EACD;;EAEiB,aAAPT,OAAO,GAAG;EACnB,WAAOA,OAAP;EACD;;EAzBiB;;;;;;;;"} -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/dom/data.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap data.js v5.0.0-beta3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 | typeof define === 'function' && define.amd ? define(factory) : 9 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Data = factory()); 10 | }(this, (function () { 'use strict'; 11 | 12 | /** 13 | * -------------------------------------------------------------------------- 14 | * Bootstrap (v5.0.0-beta3): dom/data.js 15 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 16 | * -------------------------------------------------------------------------- 17 | */ 18 | 19 | /** 20 | * ------------------------------------------------------------------------ 21 | * Constants 22 | * ------------------------------------------------------------------------ 23 | */ 24 | const elementMap = new Map(); 25 | var data = { 26 | set(element, key, instance) { 27 | if (!elementMap.has(element)) { 28 | elementMap.set(element, new Map()); 29 | } 30 | 31 | const instanceMap = elementMap.get(element); // make it clear we only want one instance per element 32 | // can be removed later when multiple key/instances are fine to be used 33 | 34 | if (!instanceMap.has(key) && instanceMap.size !== 0) { 35 | // eslint-disable-next-line no-console 36 | console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); 37 | return; 38 | } 39 | 40 | instanceMap.set(key, instance); 41 | }, 42 | 43 | get(element, key) { 44 | if (elementMap.has(element)) { 45 | return elementMap.get(element).get(key) || null; 46 | } 47 | 48 | return null; 49 | }, 50 | 51 | remove(element, key) { 52 | if (!elementMap.has(element)) { 53 | return; 54 | } 55 | 56 | const instanceMap = elementMap.get(element); 57 | instanceMap.delete(key); // free up element references if there are no instances left for an element 58 | 59 | if (instanceMap.size === 0) { 60 | elementMap.delete(element); 61 | } 62 | } 63 | 64 | }; 65 | 66 | return data; 67 | 68 | }))); 69 | //# sourceMappingURL=data.js.map 70 | -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/dom/data.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"data.js","sources":["../../src/dom/data.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.0.0-beta3): dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n"],"names":["elementMap","Map","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EAEA,MAAMA,UAAU,GAAG,IAAIC,GAAJ,EAAnB;AAEA,aAAe;EACbC,EAAAA,GAAG,CAACC,OAAD,EAAUC,GAAV,EAAeC,QAAf,EAAyB;EAC1B,QAAI,CAACL,UAAU,CAACM,GAAX,CAAeH,OAAf,CAAL,EAA8B;EAC5BH,MAAAA,UAAU,CAACE,GAAX,CAAeC,OAAf,EAAwB,IAAIF,GAAJ,EAAxB;EACD;;EAED,UAAMM,WAAW,GAAGP,UAAU,CAACQ,GAAX,CAAeL,OAAf,CAApB,CAL0B;EAQ1B;;EACA,QAAI,CAACI,WAAW,CAACD,GAAZ,CAAgBF,GAAhB,CAAD,IAAyBG,WAAW,CAACE,IAAZ,KAAqB,CAAlD,EAAqD;EACnD;EACAC,MAAAA,OAAO,CAACC,KAAR,CAAe,+EAA8EC,KAAK,CAACC,IAAN,CAAWN,WAAW,CAACO,IAAZ,EAAX,EAA+B,CAA/B,CAAkC,GAA/H;EACA;EACD;;EAEDP,IAAAA,WAAW,CAACL,GAAZ,CAAgBE,GAAhB,EAAqBC,QAArB;EACD,GAjBY;;EAmBbG,EAAAA,GAAG,CAACL,OAAD,EAAUC,GAAV,EAAe;EAChB,QAAIJ,UAAU,CAACM,GAAX,CAAeH,OAAf,CAAJ,EAA6B;EAC3B,aAAOH,UAAU,CAACQ,GAAX,CAAeL,OAAf,EAAwBK,GAAxB,CAA4BJ,GAA5B,KAAoC,IAA3C;EACD;;EAED,WAAO,IAAP;EACD,GAzBY;;EA2BbW,EAAAA,MAAM,CAACZ,OAAD,EAAUC,GAAV,EAAe;EACnB,QAAI,CAACJ,UAAU,CAACM,GAAX,CAAeH,OAAf,CAAL,EAA8B;EAC5B;EACD;;EAED,UAAMI,WAAW,GAAGP,UAAU,CAACQ,GAAX,CAAeL,OAAf,CAApB;EAEAI,IAAAA,WAAW,CAACS,MAAZ,CAAmBZ,GAAnB,EAPmB;;EAUnB,QAAIG,WAAW,CAACE,IAAZ,KAAqB,CAAzB,EAA4B;EAC1BT,MAAAA,UAAU,CAACgB,MAAX,CAAkBb,OAAlB;EACD;EACF;;EAxCY,CAAf;;;;;;;;"} -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/dom/manipulator.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap manipulator.js v5.0.0-beta3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 | typeof define === 'function' && define.amd ? define(factory) : 9 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Manipulator = factory()); 10 | }(this, (function () { 'use strict'; 11 | 12 | /** 13 | * -------------------------------------------------------------------------- 14 | * Bootstrap (v5.0.0-beta3): dom/manipulator.js 15 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 16 | * -------------------------------------------------------------------------- 17 | */ 18 | function normalizeData(val) { 19 | if (val === 'true') { 20 | return true; 21 | } 22 | 23 | if (val === 'false') { 24 | return false; 25 | } 26 | 27 | if (val === Number(val).toString()) { 28 | return Number(val); 29 | } 30 | 31 | if (val === '' || val === 'null') { 32 | return null; 33 | } 34 | 35 | return val; 36 | } 37 | 38 | function normalizeDataKey(key) { 39 | return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); 40 | } 41 | 42 | const Manipulator = { 43 | setDataAttribute(element, key, value) { 44 | element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); 45 | }, 46 | 47 | removeDataAttribute(element, key) { 48 | element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); 49 | }, 50 | 51 | getDataAttributes(element) { 52 | if (!element) { 53 | return {}; 54 | } 55 | 56 | const attributes = {}; 57 | Object.keys(element.dataset).filter(key => key.startsWith('bs')).forEach(key => { 58 | let pureKey = key.replace(/^bs/, ''); 59 | pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); 60 | attributes[pureKey] = normalizeData(element.dataset[key]); 61 | }); 62 | return attributes; 63 | }, 64 | 65 | getDataAttribute(element, key) { 66 | return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); 67 | }, 68 | 69 | offset(element) { 70 | const rect = element.getBoundingClientRect(); 71 | return { 72 | top: rect.top + document.body.scrollTop, 73 | left: rect.left + document.body.scrollLeft 74 | }; 75 | }, 76 | 77 | position(element) { 78 | return { 79 | top: element.offsetTop, 80 | left: element.offsetLeft 81 | }; 82 | } 83 | 84 | }; 85 | 86 | return Manipulator; 87 | 88 | }))); 89 | //# sourceMappingURL=manipulator.js.map 90 | -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/dom/manipulator.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"manipulator.js","sources":["../../src/dom/manipulator.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.0.0-beta3): dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(val) {\n if (val === 'true') {\n return true\n }\n\n if (val === 'false') {\n return false\n }\n\n if (val === Number(val).toString()) {\n return Number(val)\n }\n\n if (val === '' || val === 'null') {\n return null\n }\n\n return val\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n\n Object.keys(element.dataset)\n .filter(key => key.startsWith('bs'))\n .forEach(key => {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)\n attributes[pureKey] = normalizeData(element.dataset[key])\n })\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n },\n\n offset(element) {\n const rect = element.getBoundingClientRect()\n\n return {\n top: rect.top + document.body.scrollTop,\n left: rect.left + document.body.scrollLeft\n }\n },\n\n position(element) {\n return {\n top: element.offsetTop,\n left: element.offsetLeft\n }\n }\n}\n\nexport default Manipulator\n"],"names":["normalizeData","val","Number","toString","normalizeDataKey","key","replace","chr","toLowerCase","Manipulator","setDataAttribute","element","value","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","Object","keys","dataset","filter","startsWith","forEach","pureKey","charAt","slice","length","getDataAttribute","getAttribute","offset","rect","getBoundingClientRect","top","document","body","scrollTop","left","scrollLeft","position","offsetTop","offsetLeft"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;EAEA,SAASA,aAAT,CAAuBC,GAAvB,EAA4B;EAC1B,MAAIA,GAAG,KAAK,MAAZ,EAAoB;EAClB,WAAO,IAAP;EACD;;EAED,MAAIA,GAAG,KAAK,OAAZ,EAAqB;EACnB,WAAO,KAAP;EACD;;EAED,MAAIA,GAAG,KAAKC,MAAM,CAACD,GAAD,CAAN,CAAYE,QAAZ,EAAZ,EAAoC;EAClC,WAAOD,MAAM,CAACD,GAAD,CAAb;EACD;;EAED,MAAIA,GAAG,KAAK,EAAR,IAAcA,GAAG,KAAK,MAA1B,EAAkC;EAChC,WAAO,IAAP;EACD;;EAED,SAAOA,GAAP;EACD;;EAED,SAASG,gBAAT,CAA0BC,GAA1B,EAA+B;EAC7B,SAAOA,GAAG,CAACC,OAAJ,CAAY,QAAZ,EAAsBC,GAAG,IAAK,IAAGA,GAAG,CAACC,WAAJ,EAAkB,EAAnD,CAAP;EACD;;QAEKC,WAAW,GAAG;EAClBC,EAAAA,gBAAgB,CAACC,OAAD,EAAUN,GAAV,EAAeO,KAAf,EAAsB;EACpCD,IAAAA,OAAO,CAACE,YAAR,CAAsB,WAAUT,gBAAgB,CAACC,GAAD,CAAM,EAAtD,EAAyDO,KAAzD;EACD,GAHiB;;EAKlBE,EAAAA,mBAAmB,CAACH,OAAD,EAAUN,GAAV,EAAe;EAChCM,IAAAA,OAAO,CAACI,eAAR,CAAyB,WAAUX,gBAAgB,CAACC,GAAD,CAAM,EAAzD;EACD,GAPiB;;EASlBW,EAAAA,iBAAiB,CAACL,OAAD,EAAU;EACzB,QAAI,CAACA,OAAL,EAAc;EACZ,aAAO,EAAP;EACD;;EAED,UAAMM,UAAU,GAAG,EAAnB;EAEAC,IAAAA,MAAM,CAACC,IAAP,CAAYR,OAAO,CAACS,OAApB,EACGC,MADH,CACUhB,GAAG,IAAIA,GAAG,CAACiB,UAAJ,CAAe,IAAf,CADjB,EAEGC,OAFH,CAEWlB,GAAG,IAAI;EACd,UAAImB,OAAO,GAAGnB,GAAG,CAACC,OAAJ,CAAY,KAAZ,EAAmB,EAAnB,CAAd;EACAkB,MAAAA,OAAO,GAAGA,OAAO,CAACC,MAAR,CAAe,CAAf,EAAkBjB,WAAlB,KAAkCgB,OAAO,CAACE,KAAR,CAAc,CAAd,EAAiBF,OAAO,CAACG,MAAzB,CAA5C;EACAV,MAAAA,UAAU,CAACO,OAAD,CAAV,GAAsBxB,aAAa,CAACW,OAAO,CAACS,OAAR,CAAgBf,GAAhB,CAAD,CAAnC;EACD,KANH;EAQA,WAAOY,UAAP;EACD,GAzBiB;;EA2BlBW,EAAAA,gBAAgB,CAACjB,OAAD,EAAUN,GAAV,EAAe;EAC7B,WAAOL,aAAa,CAACW,OAAO,CAACkB,YAAR,CAAsB,WAAUzB,gBAAgB,CAACC,GAAD,CAAM,EAAtD,CAAD,CAApB;EACD,GA7BiB;;EA+BlByB,EAAAA,MAAM,CAACnB,OAAD,EAAU;EACd,UAAMoB,IAAI,GAAGpB,OAAO,CAACqB,qBAAR,EAAb;EAEA,WAAO;EACLC,MAAAA,GAAG,EAAEF,IAAI,CAACE,GAAL,GAAWC,QAAQ,CAACC,IAAT,CAAcC,SADzB;EAELC,MAAAA,IAAI,EAAEN,IAAI,CAACM,IAAL,GAAYH,QAAQ,CAACC,IAAT,CAAcG;EAF3B,KAAP;EAID,GAtCiB;;EAwClBC,EAAAA,QAAQ,CAAC5B,OAAD,EAAU;EAChB,WAAO;EACLsB,MAAAA,GAAG,EAAEtB,OAAO,CAAC6B,SADR;EAELH,MAAAA,IAAI,EAAE1B,OAAO,CAAC8B;EAFT,KAAP;EAID;;EA7CiB;;;;;;;;"} -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/dom/selector-engine.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap selector-engine.js v5.0.0-beta3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 | typeof define === 'function' && define.amd ? define(factory) : 9 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SelectorEngine = factory()); 10 | }(this, (function () { 'use strict'; 11 | 12 | /** 13 | * -------------------------------------------------------------------------- 14 | * Bootstrap (v5.0.0-beta3): dom/selector-engine.js 15 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 16 | * -------------------------------------------------------------------------- 17 | */ 18 | 19 | /** 20 | * ------------------------------------------------------------------------ 21 | * Constants 22 | * ------------------------------------------------------------------------ 23 | */ 24 | const NODE_TEXT = 3; 25 | const SelectorEngine = { 26 | find(selector, element = document.documentElement) { 27 | return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); 28 | }, 29 | 30 | findOne(selector, element = document.documentElement) { 31 | return Element.prototype.querySelector.call(element, selector); 32 | }, 33 | 34 | children(element, selector) { 35 | return [].concat(...element.children).filter(child => child.matches(selector)); 36 | }, 37 | 38 | parents(element, selector) { 39 | const parents = []; 40 | let ancestor = element.parentNode; 41 | 42 | while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) { 43 | if (ancestor.matches(selector)) { 44 | parents.push(ancestor); 45 | } 46 | 47 | ancestor = ancestor.parentNode; 48 | } 49 | 50 | return parents; 51 | }, 52 | 53 | prev(element, selector) { 54 | let previous = element.previousElementSibling; 55 | 56 | while (previous) { 57 | if (previous.matches(selector)) { 58 | return [previous]; 59 | } 60 | 61 | previous = previous.previousElementSibling; 62 | } 63 | 64 | return []; 65 | }, 66 | 67 | next(element, selector) { 68 | let next = element.nextElementSibling; 69 | 70 | while (next) { 71 | if (next.matches(selector)) { 72 | return [next]; 73 | } 74 | 75 | next = next.nextElementSibling; 76 | } 77 | 78 | return []; 79 | } 80 | 81 | }; 82 | 83 | return SelectorEngine; 84 | 85 | }))); 86 | //# sourceMappingURL=selector-engine.js.map 87 | -------------------------------------------------------------------------------- /sass/bootstrap/js/dist/dom/selector-engine.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"selector-engine.js","sources":["../../src/dom/selector-engine.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.0.0-beta3): dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NODE_TEXT = 3\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children)\n .filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n\n let ancestor = element.parentNode\n\n while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {\n if (ancestor.matches(selector)) {\n parents.push(ancestor)\n }\n\n ancestor = ancestor.parentNode\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n }\n}\n\nexport default SelectorEngine\n"],"names":["NODE_TEXT","SelectorEngine","find","selector","element","document","documentElement","concat","Element","prototype","querySelectorAll","call","findOne","querySelector","children","filter","child","matches","parents","ancestor","parentNode","nodeType","Node","ELEMENT_NODE","push","prev","previous","previousElementSibling","next","nextElementSibling"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EAEA,MAAMA,SAAS,GAAG,CAAlB;QAEMC,cAAc,GAAG;EACrBC,EAAAA,IAAI,CAACC,QAAD,EAAWC,OAAO,GAAGC,QAAQ,CAACC,eAA9B,EAA+C;EACjD,WAAO,GAAGC,MAAH,CAAU,GAAGC,OAAO,CAACC,SAAR,CAAkBC,gBAAlB,CAAmCC,IAAnC,CAAwCP,OAAxC,EAAiDD,QAAjD,CAAb,CAAP;EACD,GAHoB;;EAKrBS,EAAAA,OAAO,CAACT,QAAD,EAAWC,OAAO,GAAGC,QAAQ,CAACC,eAA9B,EAA+C;EACpD,WAAOE,OAAO,CAACC,SAAR,CAAkBI,aAAlB,CAAgCF,IAAhC,CAAqCP,OAArC,EAA8CD,QAA9C,CAAP;EACD,GAPoB;;EASrBW,EAAAA,QAAQ,CAACV,OAAD,EAAUD,QAAV,EAAoB;EAC1B,WAAO,GAAGI,MAAH,CAAU,GAAGH,OAAO,CAACU,QAArB,EACJC,MADI,CACGC,KAAK,IAAIA,KAAK,CAACC,OAAN,CAAcd,QAAd,CADZ,CAAP;EAED,GAZoB;;EAcrBe,EAAAA,OAAO,CAACd,OAAD,EAAUD,QAAV,EAAoB;EACzB,UAAMe,OAAO,GAAG,EAAhB;EAEA,QAAIC,QAAQ,GAAGf,OAAO,CAACgB,UAAvB;;EAEA,WAAOD,QAAQ,IAAIA,QAAQ,CAACE,QAAT,KAAsBC,IAAI,CAACC,YAAvC,IAAuDJ,QAAQ,CAACE,QAAT,KAAsBrB,SAApF,EAA+F;EAC7F,UAAImB,QAAQ,CAACF,OAAT,CAAiBd,QAAjB,CAAJ,EAAgC;EAC9Be,QAAAA,OAAO,CAACM,IAAR,CAAaL,QAAb;EACD;;EAEDA,MAAAA,QAAQ,GAAGA,QAAQ,CAACC,UAApB;EACD;;EAED,WAAOF,OAAP;EACD,GA5BoB;;EA8BrBO,EAAAA,IAAI,CAACrB,OAAD,EAAUD,QAAV,EAAoB;EACtB,QAAIuB,QAAQ,GAAGtB,OAAO,CAACuB,sBAAvB;;EAEA,WAAOD,QAAP,EAAiB;EACf,UAAIA,QAAQ,CAACT,OAAT,CAAiBd,QAAjB,CAAJ,EAAgC;EAC9B,eAAO,CAACuB,QAAD,CAAP;EACD;;EAEDA,MAAAA,QAAQ,GAAGA,QAAQ,CAACC,sBAApB;EACD;;EAED,WAAO,EAAP;EACD,GA1CoB;;EA4CrBC,EAAAA,IAAI,CAACxB,OAAD,EAAUD,QAAV,EAAoB;EACtB,QAAIyB,IAAI,GAAGxB,OAAO,CAACyB,kBAAnB;;EAEA,WAAOD,IAAP,EAAa;EACX,UAAIA,IAAI,CAACX,OAAL,CAAad,QAAb,CAAJ,EAA4B;EAC1B,eAAO,CAACyB,IAAD,CAAP;EACD;;EAEDA,MAAAA,IAAI,GAAGA,IAAI,CAACC,kBAAZ;EACD;;EAED,WAAO,EAAP;EACD;;EAxDoB;;;;;;;;"} -------------------------------------------------------------------------------- /sass/bootstrap/js/src/alert.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): alert.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | import { 9 | defineJQueryPlugin, 10 | emulateTransitionEnd, 11 | getElementFromSelector, 12 | getTransitionDurationFromElement 13 | } from './util/index' 14 | import Data from './dom/data' 15 | import EventHandler from './dom/event-handler' 16 | import BaseComponent from './base-component' 17 | 18 | /** 19 | * ------------------------------------------------------------------------ 20 | * Constants 21 | * ------------------------------------------------------------------------ 22 | */ 23 | 24 | const NAME = 'alert' 25 | const DATA_KEY = 'bs.alert' 26 | const EVENT_KEY = `.${DATA_KEY}` 27 | const DATA_API_KEY = '.data-api' 28 | 29 | const SELECTOR_DISMISS = '[data-bs-dismiss="alert"]' 30 | 31 | const EVENT_CLOSE = `close${EVENT_KEY}` 32 | const EVENT_CLOSED = `closed${EVENT_KEY}` 33 | const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` 34 | 35 | const CLASS_NAME_ALERT = 'alert' 36 | const CLASS_NAME_FADE = 'fade' 37 | const CLASS_NAME_SHOW = 'show' 38 | 39 | /** 40 | * ------------------------------------------------------------------------ 41 | * Class Definition 42 | * ------------------------------------------------------------------------ 43 | */ 44 | 45 | class Alert extends BaseComponent { 46 | // Getters 47 | 48 | static get DATA_KEY() { 49 | return DATA_KEY 50 | } 51 | 52 | // Public 53 | 54 | close(element) { 55 | const rootElement = element ? this._getRootElement(element) : this._element 56 | const customEvent = this._triggerCloseEvent(rootElement) 57 | 58 | if (customEvent === null || customEvent.defaultPrevented) { 59 | return 60 | } 61 | 62 | this._removeElement(rootElement) 63 | } 64 | 65 | // Private 66 | 67 | _getRootElement(element) { 68 | return getElementFromSelector(element) || element.closest(`.${CLASS_NAME_ALERT}`) 69 | } 70 | 71 | _triggerCloseEvent(element) { 72 | return EventHandler.trigger(element, EVENT_CLOSE) 73 | } 74 | 75 | _removeElement(element) { 76 | element.classList.remove(CLASS_NAME_SHOW) 77 | 78 | if (!element.classList.contains(CLASS_NAME_FADE)) { 79 | this._destroyElement(element) 80 | return 81 | } 82 | 83 | const transitionDuration = getTransitionDurationFromElement(element) 84 | 85 | EventHandler.one(element, 'transitionend', () => this._destroyElement(element)) 86 | emulateTransitionEnd(element, transitionDuration) 87 | } 88 | 89 | _destroyElement(element) { 90 | if (element.parentNode) { 91 | element.parentNode.removeChild(element) 92 | } 93 | 94 | EventHandler.trigger(element, EVENT_CLOSED) 95 | } 96 | 97 | // Static 98 | 99 | static jQueryInterface(config) { 100 | return this.each(function () { 101 | let data = Data.get(this, DATA_KEY) 102 | 103 | if (!data) { 104 | data = new Alert(this) 105 | } 106 | 107 | if (config === 'close') { 108 | data[config](this) 109 | } 110 | }) 111 | } 112 | 113 | static handleDismiss(alertInstance) { 114 | return function (event) { 115 | if (event) { 116 | event.preventDefault() 117 | } 118 | 119 | alertInstance.close(this) 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * ------------------------------------------------------------------------ 126 | * Data Api implementation 127 | * ------------------------------------------------------------------------ 128 | */ 129 | 130 | EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert())) 131 | 132 | /** 133 | * ------------------------------------------------------------------------ 134 | * jQuery 135 | * ------------------------------------------------------------------------ 136 | * add .Alert to jQuery only if jQuery is present 137 | */ 138 | 139 | defineJQueryPlugin(NAME, Alert) 140 | 141 | export default Alert 142 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/base-component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): base-component.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | import Data from './dom/data' 9 | 10 | /** 11 | * ------------------------------------------------------------------------ 12 | * Constants 13 | * ------------------------------------------------------------------------ 14 | */ 15 | 16 | const VERSION = '5.0.0-beta3' 17 | 18 | class BaseComponent { 19 | constructor(element) { 20 | element = typeof element === 'string' ? document.querySelector(element) : element 21 | 22 | if (!element) { 23 | return 24 | } 25 | 26 | this._element = element 27 | Data.set(this._element, this.constructor.DATA_KEY, this) 28 | } 29 | 30 | dispose() { 31 | Data.remove(this._element, this.constructor.DATA_KEY) 32 | this._element = null 33 | } 34 | 35 | /** Static */ 36 | 37 | static getInstance(element) { 38 | return Data.get(element, this.DATA_KEY) 39 | } 40 | 41 | static get VERSION() { 42 | return VERSION 43 | } 44 | } 45 | 46 | export default BaseComponent 47 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/button.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): button.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | import { defineJQueryPlugin } from './util/index' 9 | import Data from './dom/data' 10 | import EventHandler from './dom/event-handler' 11 | import BaseComponent from './base-component' 12 | 13 | /** 14 | * ------------------------------------------------------------------------ 15 | * Constants 16 | * ------------------------------------------------------------------------ 17 | */ 18 | 19 | const NAME = 'button' 20 | const DATA_KEY = 'bs.button' 21 | const EVENT_KEY = `.${DATA_KEY}` 22 | const DATA_API_KEY = '.data-api' 23 | 24 | const CLASS_NAME_ACTIVE = 'active' 25 | 26 | const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]' 27 | 28 | const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` 29 | 30 | /** 31 | * ------------------------------------------------------------------------ 32 | * Class Definition 33 | * ------------------------------------------------------------------------ 34 | */ 35 | 36 | class Button extends BaseComponent { 37 | // Getters 38 | 39 | static get DATA_KEY() { 40 | return DATA_KEY 41 | } 42 | 43 | // Public 44 | 45 | toggle() { 46 | // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method 47 | this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE)) 48 | } 49 | 50 | // Static 51 | 52 | static jQueryInterface(config) { 53 | return this.each(function () { 54 | let data = Data.get(this, DATA_KEY) 55 | 56 | if (!data) { 57 | data = new Button(this) 58 | } 59 | 60 | if (config === 'toggle') { 61 | data[config]() 62 | } 63 | }) 64 | } 65 | } 66 | 67 | /** 68 | * ------------------------------------------------------------------------ 69 | * Data Api implementation 70 | * ------------------------------------------------------------------------ 71 | */ 72 | 73 | EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { 74 | event.preventDefault() 75 | 76 | const button = event.target.closest(SELECTOR_DATA_TOGGLE) 77 | 78 | let data = Data.get(button, DATA_KEY) 79 | if (!data) { 80 | data = new Button(button) 81 | } 82 | 83 | data.toggle() 84 | }) 85 | 86 | /** 87 | * ------------------------------------------------------------------------ 88 | * jQuery 89 | * ------------------------------------------------------------------------ 90 | * add .Button to jQuery only if jQuery is present 91 | */ 92 | 93 | defineJQueryPlugin(NAME, Button) 94 | 95 | export default Button 96 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/dom/data.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): dom/data.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | /** 9 | * ------------------------------------------------------------------------ 10 | * Constants 11 | * ------------------------------------------------------------------------ 12 | */ 13 | 14 | const elementMap = new Map() 15 | 16 | export default { 17 | set(element, key, instance) { 18 | if (!elementMap.has(element)) { 19 | elementMap.set(element, new Map()) 20 | } 21 | 22 | const instanceMap = elementMap.get(element) 23 | 24 | // make it clear we only want one instance per element 25 | // can be removed later when multiple key/instances are fine to be used 26 | if (!instanceMap.has(key) && instanceMap.size !== 0) { 27 | // eslint-disable-next-line no-console 28 | console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`) 29 | return 30 | } 31 | 32 | instanceMap.set(key, instance) 33 | }, 34 | 35 | get(element, key) { 36 | if (elementMap.has(element)) { 37 | return elementMap.get(element).get(key) || null 38 | } 39 | 40 | return null 41 | }, 42 | 43 | remove(element, key) { 44 | if (!elementMap.has(element)) { 45 | return 46 | } 47 | 48 | const instanceMap = elementMap.get(element) 49 | 50 | instanceMap.delete(key) 51 | 52 | // free up element references if there are no instances left for an element 53 | if (instanceMap.size === 0) { 54 | elementMap.delete(element) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/dom/manipulator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): dom/manipulator.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | function normalizeData(val) { 9 | if (val === 'true') { 10 | return true 11 | } 12 | 13 | if (val === 'false') { 14 | return false 15 | } 16 | 17 | if (val === Number(val).toString()) { 18 | return Number(val) 19 | } 20 | 21 | if (val === '' || val === 'null') { 22 | return null 23 | } 24 | 25 | return val 26 | } 27 | 28 | function normalizeDataKey(key) { 29 | return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`) 30 | } 31 | 32 | const Manipulator = { 33 | setDataAttribute(element, key, value) { 34 | element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value) 35 | }, 36 | 37 | removeDataAttribute(element, key) { 38 | element.removeAttribute(`data-bs-${normalizeDataKey(key)}`) 39 | }, 40 | 41 | getDataAttributes(element) { 42 | if (!element) { 43 | return {} 44 | } 45 | 46 | const attributes = {} 47 | 48 | Object.keys(element.dataset) 49 | .filter(key => key.startsWith('bs')) 50 | .forEach(key => { 51 | let pureKey = key.replace(/^bs/, '') 52 | pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length) 53 | attributes[pureKey] = normalizeData(element.dataset[key]) 54 | }) 55 | 56 | return attributes 57 | }, 58 | 59 | getDataAttribute(element, key) { 60 | return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)) 61 | }, 62 | 63 | offset(element) { 64 | const rect = element.getBoundingClientRect() 65 | 66 | return { 67 | top: rect.top + document.body.scrollTop, 68 | left: rect.left + document.body.scrollLeft 69 | } 70 | }, 71 | 72 | position(element) { 73 | return { 74 | top: element.offsetTop, 75 | left: element.offsetLeft 76 | } 77 | } 78 | } 79 | 80 | export default Manipulator 81 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/dom/selector-engine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): dom/selector-engine.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | /** 9 | * ------------------------------------------------------------------------ 10 | * Constants 11 | * ------------------------------------------------------------------------ 12 | */ 13 | 14 | const NODE_TEXT = 3 15 | 16 | const SelectorEngine = { 17 | find(selector, element = document.documentElement) { 18 | return [].concat(...Element.prototype.querySelectorAll.call(element, selector)) 19 | }, 20 | 21 | findOne(selector, element = document.documentElement) { 22 | return Element.prototype.querySelector.call(element, selector) 23 | }, 24 | 25 | children(element, selector) { 26 | return [].concat(...element.children) 27 | .filter(child => child.matches(selector)) 28 | }, 29 | 30 | parents(element, selector) { 31 | const parents = [] 32 | 33 | let ancestor = element.parentNode 34 | 35 | while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) { 36 | if (ancestor.matches(selector)) { 37 | parents.push(ancestor) 38 | } 39 | 40 | ancestor = ancestor.parentNode 41 | } 42 | 43 | return parents 44 | }, 45 | 46 | prev(element, selector) { 47 | let previous = element.previousElementSibling 48 | 49 | while (previous) { 50 | if (previous.matches(selector)) { 51 | return [previous] 52 | } 53 | 54 | previous = previous.previousElementSibling 55 | } 56 | 57 | return [] 58 | }, 59 | 60 | next(element, selector) { 61 | let next = element.nextElementSibling 62 | 63 | while (next) { 64 | if (next.matches(selector)) { 65 | return [next] 66 | } 67 | 68 | next = next.nextElementSibling 69 | } 70 | 71 | return [] 72 | } 73 | } 74 | 75 | export default SelectorEngine 76 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/util/sanitizer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): util/sanitizer.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | const uriAttrs = new Set([ 9 | 'background', 10 | 'cite', 11 | 'href', 12 | 'itemtype', 13 | 'longdesc', 14 | 'poster', 15 | 'src', 16 | 'xlink:href' 17 | ]) 18 | 19 | const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i 20 | 21 | /** 22 | * A pattern that recognizes a commonly useful subset of URLs that are safe. 23 | * 24 | * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts 25 | */ 26 | const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/i 27 | 28 | /** 29 | * A pattern that matches safe data URLs. Only matches image, video and audio types. 30 | * 31 | * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts 32 | */ 33 | const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i 34 | 35 | const allowedAttribute = (attr, allowedAttributeList) => { 36 | const attrName = attr.nodeName.toLowerCase() 37 | 38 | if (allowedAttributeList.includes(attrName)) { 39 | if (uriAttrs.has(attrName)) { 40 | return Boolean(SAFE_URL_PATTERN.test(attr.nodeValue) || DATA_URL_PATTERN.test(attr.nodeValue)) 41 | } 42 | 43 | return true 44 | } 45 | 46 | const regExp = allowedAttributeList.filter(attrRegex => attrRegex instanceof RegExp) 47 | 48 | // Check if a regular expression validates the attribute. 49 | for (let i = 0, len = regExp.length; i < len; i++) { 50 | if (regExp[i].test(attrName)) { 51 | return true 52 | } 53 | } 54 | 55 | return false 56 | } 57 | 58 | export const DefaultAllowlist = { 59 | // Global attributes allowed on any supplied element below. 60 | '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], 61 | a: ['target', 'href', 'title', 'rel'], 62 | area: [], 63 | b: [], 64 | br: [], 65 | col: [], 66 | code: [], 67 | div: [], 68 | em: [], 69 | hr: [], 70 | h1: [], 71 | h2: [], 72 | h3: [], 73 | h4: [], 74 | h5: [], 75 | h6: [], 76 | i: [], 77 | img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], 78 | li: [], 79 | ol: [], 80 | p: [], 81 | pre: [], 82 | s: [], 83 | small: [], 84 | span: [], 85 | sub: [], 86 | sup: [], 87 | strong: [], 88 | u: [], 89 | ul: [] 90 | } 91 | 92 | export function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) { 93 | if (!unsafeHtml.length) { 94 | return unsafeHtml 95 | } 96 | 97 | if (sanitizeFn && typeof sanitizeFn === 'function') { 98 | return sanitizeFn(unsafeHtml) 99 | } 100 | 101 | const domParser = new window.DOMParser() 102 | const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html') 103 | const allowlistKeys = Object.keys(allowList) 104 | const elements = [].concat(...createdDocument.body.querySelectorAll('*')) 105 | 106 | for (let i = 0, len = elements.length; i < len; i++) { 107 | const el = elements[i] 108 | const elName = el.nodeName.toLowerCase() 109 | 110 | if (!allowlistKeys.includes(elName)) { 111 | el.parentNode.removeChild(el) 112 | 113 | continue 114 | } 115 | 116 | const attributeList = [].concat(...el.attributes) 117 | const allowedAttributes = [].concat(allowList['*'] || [], allowList[elName] || []) 118 | 119 | attributeList.forEach(attr => { 120 | if (!allowedAttribute(attr, allowedAttributes)) { 121 | el.removeAttribute(attr.nodeName) 122 | } 123 | }) 124 | } 125 | 126 | return createdDocument.body.innerHTML 127 | } 128 | -------------------------------------------------------------------------------- /sass/bootstrap/js/src/util/scrollbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * -------------------------------------------------------------------------- 3 | * Bootstrap (v5.0.0-beta3): util/scrollBar.js 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 5 | * -------------------------------------------------------------------------- 6 | */ 7 | 8 | import SelectorEngine from '../dom/selector-engine' 9 | import Manipulator from '../dom/manipulator' 10 | 11 | const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed' 12 | const SELECTOR_STICKY_CONTENT = '.sticky-top' 13 | 14 | const getWidth = () => { 15 | // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes 16 | const documentWidth = document.documentElement.clientWidth 17 | return Math.abs(window.innerWidth - documentWidth) 18 | } 19 | 20 | const hide = (width = getWidth()) => { 21 | document.body.style.overflow = 'hidden' 22 | _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width) 23 | _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width) 24 | _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width) 25 | } 26 | 27 | const _setElementAttributes = (selector, styleProp, callback) => { 28 | const scrollbarWidth = getWidth() 29 | SelectorEngine.find(selector) 30 | .forEach(element => { 31 | if (element !== document.body && window.innerWidth > element.clientWidth + scrollbarWidth) { 32 | return 33 | } 34 | 35 | const actualValue = element.style[styleProp] 36 | const calculatedValue = window.getComputedStyle(element)[styleProp] 37 | Manipulator.setDataAttribute(element, styleProp, actualValue) 38 | element.style[styleProp] = callback(Number.parseFloat(calculatedValue)) + 'px' 39 | }) 40 | } 41 | 42 | const reset = () => { 43 | document.body.style.overflow = 'auto' 44 | _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight') 45 | _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight') 46 | _resetElementAttributes('body', 'paddingRight') 47 | } 48 | 49 | const _resetElementAttributes = (selector, styleProp) => { 50 | SelectorEngine.find(selector).forEach(element => { 51 | const value = Manipulator.getDataAttribute(element, styleProp) 52 | if (typeof value === 'undefined' && element === document.body) { 53 | element.style.removeProperty(styleProp) 54 | } else { 55 | Manipulator.removeDataAttribute(element, styleProp) 56 | element.style[styleProp] = value 57 | } 58 | }) 59 | } 60 | 61 | const isBodyOverflowing = () => { 62 | return getWidth() > 0 63 | } 64 | 65 | export { 66 | getWidth, 67 | hide, 68 | isBodyOverflowing, 69 | reset 70 | } 71 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_accordion.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .accordion-button { 6 | position: relative; 7 | display: flex; 8 | align-items: center; 9 | width: 100%; 10 | padding: $accordion-button-padding-y $accordion-button-padding-x; 11 | @include font-size($font-size-base); 12 | color: $accordion-button-color; 13 | text-align: left; // Reset button style 14 | background-color: $accordion-button-bg; 15 | border: 0; 16 | @include border-radius(0); 17 | overflow-anchor: none; 18 | @include transition($accordion-transition); 19 | 20 | &:not(.collapsed) { 21 | color: $accordion-button-active-color; 22 | background-color: $accordion-button-active-bg; 23 | box-shadow: inset 0 ($accordion-border-width * -1) 0 $accordion-border-color; 24 | 25 | &::after { 26 | background-image: escape-svg($accordion-button-active-icon); 27 | transform: $accordion-icon-transform; 28 | } 29 | } 30 | 31 | // Accordion icon 32 | &::after { 33 | flex-shrink: 0; 34 | width: $accordion-icon-width; 35 | height: $accordion-icon-width; 36 | margin-left: auto; 37 | content: ""; 38 | background-image: escape-svg($accordion-button-icon); 39 | background-repeat: no-repeat; 40 | background-size: $accordion-icon-width; 41 | @include transition($accordion-icon-transition); 42 | } 43 | 44 | &:hover { 45 | z-index: 2; 46 | } 47 | 48 | &:focus { 49 | z-index: 3; 50 | border-color: $accordion-button-focus-border-color; 51 | outline: 0; 52 | box-shadow: $accordion-button-focus-box-shadow; 53 | } 54 | } 55 | 56 | .accordion-header { 57 | margin-bottom: 0; 58 | } 59 | 60 | .accordion-item { 61 | margin-bottom: -$accordion-border-width; 62 | background-color: $accordion-bg; 63 | border: $accordion-border-width solid $accordion-border-color; 64 | 65 | &:first-of-type { 66 | @include border-top-radius($accordion-border-radius); 67 | 68 | .accordion-button { 69 | @include border-top-radius($accordion-inner-border-radius); 70 | } 71 | } 72 | 73 | // Only set a border-radius on the last item if the accordion is collapsed 74 | &:last-of-type { 75 | margin-bottom: 0; 76 | @include border-bottom-radius($accordion-border-radius); 77 | 78 | .accordion-button { 79 | &.collapsed { 80 | @include border-bottom-radius($accordion-inner-border-radius); 81 | } 82 | } 83 | 84 | .accordion-collapse { 85 | @include border-bottom-radius($accordion-border-radius); 86 | } 87 | } 88 | } 89 | 90 | .accordion-body { 91 | padding: $accordion-body-padding-y $accordion-body-padding-x; 92 | } 93 | 94 | 95 | // Flush accordion items 96 | // 97 | // Remove borders and border-radius to keep accordion items edge-to-edge. 98 | 99 | .accordion-flush { 100 | .accordion-collapse { 101 | border-width: 0; 102 | } 103 | 104 | .accordion-item { 105 | border-right: 0; 106 | border-left: 0; 107 | @include border-radius(0); 108 | 109 | &:first-child { border-top: 0; } 110 | &:last-child { border-bottom: 0; } 111 | 112 | .accordion-button { 113 | @include border-radius(0); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_alert.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .alert { 6 | position: relative; 7 | padding: $alert-padding-y $alert-padding-x; 8 | margin-bottom: $alert-margin-bottom; 9 | border: $alert-border-width solid transparent; 10 | @include border-radius($alert-border-radius); 11 | } 12 | 13 | // Headings for larger alerts 14 | .alert-heading { 15 | // Specified to prevent conflicts of changing $headings-color 16 | color: inherit; 17 | } 18 | 19 | // Provide class for links that match alerts 20 | .alert-link { 21 | font-weight: $alert-link-font-weight; 22 | } 23 | 24 | 25 | // Dismissible alerts 26 | // 27 | // Expand the right padding and account for the close button's positioning. 28 | 29 | .alert-dismissible { 30 | padding-right: $alert-dismissible-padding-r; 31 | 32 | // Adjust close link position 33 | .btn-close { 34 | position: absolute; 35 | top: 0; 36 | right: 0; 37 | z-index: $stretched-link-z-index + 1; 38 | padding: $alert-padding-y * 1.25 $alert-padding-x; 39 | } 40 | } 41 | 42 | 43 | // scss-docs-start alert-modifiers 44 | // Generate contextual modifier classes for colorizing the alert. 45 | 46 | @each $state, $value in $theme-colors { 47 | $alert-background: shift-color($value, $alert-bg-scale); 48 | $alert-border: shift-color($value, $alert-border-scale); 49 | $alert-color: shift-color($value, $alert-color-scale); 50 | @if (contrast-ratio($alert-background, $alert-color) < $min-contrast-ratio) { 51 | $alert-color: mix($value, color-contrast($alert-background), abs($alert-color-scale)); 52 | } 53 | .alert-#{$state} { 54 | @include alert-variant($alert-background, $alert-border, $alert-color); 55 | } 56 | } 57 | // scss-docs-end alert-modifiers 58 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_badge.scss: -------------------------------------------------------------------------------- 1 | // Base class 2 | // 3 | // Requires one of the contextual, color modifier classes for `color` and 4 | // `background-color`. 5 | 6 | .badge { 7 | display: inline-block; 8 | padding: $badge-padding-y $badge-padding-x; 9 | @include font-size($badge-font-size); 10 | font-weight: $badge-font-weight; 11 | line-height: 1; 12 | color: $badge-color; 13 | text-align: center; 14 | white-space: nowrap; 15 | vertical-align: baseline; 16 | @include border-radius($badge-border-radius); 17 | @include gradient-bg(); 18 | 19 | // Empty badges collapse automatically 20 | &:empty { 21 | display: none; 22 | } 23 | } 24 | 25 | // Quick fix for badges in buttons 26 | .btn .badge { 27 | position: relative; 28 | top: -1px; 29 | } 30 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_breadcrumb.scss: -------------------------------------------------------------------------------- 1 | .breadcrumb { 2 | display: flex; 3 | flex-wrap: wrap; 4 | padding: $breadcrumb-padding-y $breadcrumb-padding-x; 5 | margin-bottom: $breadcrumb-margin-bottom; 6 | @include font-size($breadcrumb-font-size); 7 | list-style: none; 8 | background-color: $breadcrumb-bg; 9 | @include border-radius($breadcrumb-border-radius); 10 | } 11 | 12 | .breadcrumb-item { 13 | // The separator between breadcrumbs (by default, a forward-slash: "/") 14 | + .breadcrumb-item { 15 | padding-left: $breadcrumb-item-padding-x; 16 | 17 | &::before { 18 | float: left; // Suppress inline spacings and underlining of the separator 19 | padding-right: $breadcrumb-item-padding-x; 20 | color: $breadcrumb-divider-color; 21 | content: var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider)) #{"/* rtl:"} var(--#{$variable-prefix}breadcrumb-divider, escape-svg($breadcrumb-divider-flipped)) #{"*/"}; 22 | } 23 | } 24 | 25 | &.active { 26 | color: $breadcrumb-active-color; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_button-group.scss: -------------------------------------------------------------------------------- 1 | // Make the div behave like a button 2 | .btn-group, 3 | .btn-group-vertical { 4 | position: relative; 5 | display: inline-flex; 6 | vertical-align: middle; // match .btn alignment given font-size hack above 7 | 8 | > .btn { 9 | position: relative; 10 | flex: 1 1 auto; 11 | } 12 | 13 | // Bring the hover, focused, and "active" buttons to the front to overlay 14 | // the borders properly 15 | > .btn-check:checked + .btn, 16 | > .btn-check:focus + .btn, 17 | > .btn:hover, 18 | > .btn:focus, 19 | > .btn:active, 20 | > .btn.active { 21 | z-index: 1; 22 | } 23 | } 24 | 25 | // Optional: Group multiple button groups together for a toolbar 26 | .btn-toolbar { 27 | display: flex; 28 | flex-wrap: wrap; 29 | justify-content: flex-start; 30 | 31 | .input-group { 32 | width: auto; 33 | } 34 | } 35 | 36 | .btn-group { 37 | // Prevent double borders when buttons are next to each other 38 | > .btn:not(:first-child), 39 | > .btn-group:not(:first-child) { 40 | margin-left: -$btn-border-width; 41 | } 42 | 43 | // Reset rounded corners 44 | > .btn:not(:last-child):not(.dropdown-toggle), 45 | > .btn-group:not(:last-child) > .btn { 46 | @include border-end-radius(0); 47 | } 48 | 49 | // The left radius should be 0 if the button is: 50 | // - the "third or more" child 51 | // - the second child and the previous element isn't `.btn-check` (making it the first child visually) 52 | // - part of a btn-group which isn't the first child 53 | > .btn:nth-child(n + 3), 54 | > :not(.btn-check) + .btn, 55 | > .btn-group:not(:first-child) > .btn { 56 | @include border-start-radius(0); 57 | } 58 | } 59 | 60 | // Sizing 61 | // 62 | // Remix the default button sizing classes into new ones for easier manipulation. 63 | 64 | .btn-group-sm > .btn { @extend .btn-sm; } 65 | .btn-group-lg > .btn { @extend .btn-lg; } 66 | 67 | 68 | // 69 | // Split button dropdowns 70 | // 71 | 72 | .dropdown-toggle-split { 73 | padding-right: $btn-padding-x * .75; 74 | padding-left: $btn-padding-x * .75; 75 | 76 | &::after, 77 | .dropup &::after, 78 | .dropend &::after { 79 | margin-left: 0; 80 | } 81 | 82 | .dropstart &::before { 83 | margin-right: 0; 84 | } 85 | } 86 | 87 | .btn-sm + .dropdown-toggle-split { 88 | padding-right: $btn-padding-x-sm * .75; 89 | padding-left: $btn-padding-x-sm * .75; 90 | } 91 | 92 | .btn-lg + .dropdown-toggle-split { 93 | padding-right: $btn-padding-x-lg * .75; 94 | padding-left: $btn-padding-x-lg * .75; 95 | } 96 | 97 | 98 | // The clickable button for toggling the menu 99 | // Set the same inset shadow as the :active state 100 | .btn-group.show .dropdown-toggle { 101 | @include box-shadow($btn-active-box-shadow); 102 | 103 | // Show no shadow for `.btn-link` since it has no other button styles. 104 | &.btn-link { 105 | @include box-shadow(none); 106 | } 107 | } 108 | 109 | 110 | // 111 | // Vertical button groups 112 | // 113 | 114 | .btn-group-vertical { 115 | flex-direction: column; 116 | align-items: flex-start; 117 | justify-content: center; 118 | 119 | > .btn, 120 | > .btn-group { 121 | width: 100%; 122 | } 123 | 124 | > .btn:not(:first-child), 125 | > .btn-group:not(:first-child) { 126 | margin-top: -$btn-border-width; 127 | } 128 | 129 | // Reset rounded corners 130 | > .btn:not(:last-child):not(.dropdown-toggle), 131 | > .btn-group:not(:last-child) > .btn { 132 | @include border-bottom-radius(0); 133 | } 134 | 135 | > .btn ~ .btn, 136 | > .btn-group:not(:first-child) > .btn { 137 | @include border-top-radius(0); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_buttons.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Base styles 3 | // 4 | 5 | .btn { 6 | display: inline-block; 7 | font-family: $btn-font-family; 8 | font-weight: $btn-font-weight; 9 | line-height: $btn-line-height; 10 | color: $body-color; 11 | text-align: center; 12 | text-decoration: if($link-decoration == none, null, none); 13 | white-space: $btn-white-space; 14 | vertical-align: middle; 15 | cursor: if($enable-button-pointers, pointer, null); 16 | user-select: none; 17 | background-color: transparent; 18 | border: $btn-border-width solid transparent; 19 | @include button-size($btn-padding-y, $btn-padding-x, $btn-font-size, $btn-border-radius); 20 | @include transition($btn-transition); 21 | 22 | &:hover { 23 | color: $body-color; 24 | text-decoration: if($link-hover-decoration == underline, none, null); 25 | } 26 | 27 | .btn-check:focus + &, 28 | &:focus { 29 | outline: 0; 30 | box-shadow: $btn-focus-box-shadow; 31 | } 32 | 33 | .btn-check:checked + &, 34 | .btn-check:active + &, 35 | &:active, 36 | &.active { 37 | @include box-shadow($btn-active-box-shadow); 38 | 39 | &:focus { 40 | @include box-shadow($btn-focus-box-shadow, $btn-active-box-shadow); 41 | } 42 | } 43 | 44 | &:disabled, 45 | &.disabled, 46 | fieldset:disabled & { 47 | pointer-events: none; 48 | opacity: $btn-disabled-opacity; 49 | @include box-shadow(none); 50 | } 51 | } 52 | 53 | 54 | // 55 | // Alternate buttons 56 | // 57 | 58 | // scss-docs-start btn-variant-loops 59 | @each $color, $value in $theme-colors { 60 | .btn-#{$color} { 61 | @include button-variant($value, $value); 62 | } 63 | } 64 | 65 | @each $color, $value in $theme-colors { 66 | .btn-outline-#{$color} { 67 | @include button-outline-variant($value); 68 | } 69 | } 70 | // scss-docs-end btn-variant-loops 71 | 72 | 73 | // 74 | // Link buttons 75 | // 76 | 77 | // Make a button look and behave like a link 78 | .btn-link { 79 | font-weight: $font-weight-normal; 80 | color: $btn-link-color; 81 | text-decoration: $link-decoration; 82 | 83 | &:hover { 84 | color: $btn-link-hover-color; 85 | text-decoration: $link-hover-decoration; 86 | } 87 | 88 | &:focus { 89 | text-decoration: $link-hover-decoration; 90 | } 91 | 92 | &:disabled, 93 | &.disabled { 94 | color: $btn-link-disabled-color; 95 | } 96 | 97 | // No need for an active state here 98 | } 99 | 100 | 101 | // 102 | // Button Sizes 103 | // 104 | 105 | .btn-lg { 106 | @include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg); 107 | } 108 | 109 | .btn-sm { 110 | @include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm); 111 | } 112 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_close.scss: -------------------------------------------------------------------------------- 1 | // transparent background and border properties included for button version. 2 | // iOS requires the button element instead of an anchor tag. 3 | // If you want the anchor version, it requires `href="#"`. 4 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile 5 | 6 | .btn-close { 7 | box-sizing: content-box; 8 | width: $btn-close-width; 9 | height: $btn-close-height; 10 | padding: $btn-close-padding-y $btn-close-padding-x; 11 | color: $btn-close-color; 12 | background: transparent escape-svg($btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements 13 | border: 0; // for button elements 14 | @include border-radius(); 15 | opacity: $btn-close-opacity; 16 | 17 | // Override 's hover style 18 | &:hover { 19 | color: $btn-close-color; 20 | text-decoration: none; 21 | opacity: $btn-close-hover-opacity; 22 | } 23 | 24 | &:focus { 25 | outline: 0; 26 | box-shadow: $btn-close-focus-shadow; 27 | opacity: $btn-close-focus-opacity; 28 | } 29 | 30 | &:disabled, 31 | &.disabled { 32 | pointer-events: none; 33 | user-select: none; 34 | opacity: $btn-close-disabled-opacity; 35 | } 36 | } 37 | 38 | .btn-close-white { 39 | filter: $btn-close-white-filter; 40 | } 41 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_containers.scss: -------------------------------------------------------------------------------- 1 | // Container widths 2 | // 3 | // Set the container width, and override it for fixed navbars in media queries. 4 | 5 | @if $enable-grid-classes { 6 | // Single container class with breakpoint max-widths 7 | .container, 8 | // 100% wide container at all breakpoints 9 | .container-fluid { 10 | @include make-container(); 11 | } 12 | 13 | // Responsive containers that are 100% wide until a breakpoint 14 | @each $breakpoint, $container-max-width in $container-max-widths { 15 | .container-#{$breakpoint} { 16 | @extend .container-fluid; 17 | } 18 | 19 | @include media-breakpoint-up($breakpoint, $grid-breakpoints) { 20 | %responsive-container-#{$breakpoint} { 21 | max-width: $container-max-width; 22 | } 23 | 24 | // Extend each breakpoint which is smaller or equal to the current breakpoint 25 | $extend-breakpoint: true; 26 | 27 | @each $name, $width in $grid-breakpoints { 28 | @if ($extend-breakpoint) { 29 | .container#{breakpoint-infix($name, $grid-breakpoints)} { 30 | @extend %responsive-container-#{$breakpoint}; 31 | } 32 | 33 | // Once the current breakpoint is reached, stop extending 34 | @if ($breakpoint == $name) { 35 | $extend-breakpoint: false; 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_forms.scss: -------------------------------------------------------------------------------- 1 | @import "forms/labels"; 2 | @import "forms/form-text"; 3 | @import "forms/form-control"; 4 | @import "forms/form-select"; 5 | @import "forms/form-check"; 6 | @import "forms/form-range"; 7 | @import "forms/floating-labels"; 8 | @import "forms/input-group"; 9 | @import "forms/validation"; 10 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_grid.scss: -------------------------------------------------------------------------------- 1 | // Row 2 | // 3 | // Rows contain your columns. 4 | 5 | @if $enable-grid-classes { 6 | .row { 7 | @include make-row(); 8 | 9 | > * { 10 | @include make-col-ready(); 11 | } 12 | } 13 | } 14 | 15 | 16 | // Columns 17 | // 18 | // Common styles for small and large grid columns 19 | 20 | @if $enable-grid-classes { 21 | @include make-grid-columns(); 22 | } 23 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_helpers.scss: -------------------------------------------------------------------------------- 1 | @import "helpers/clearfix"; 2 | @import "helpers/colored-links"; 3 | @import "helpers/ratio"; 4 | @import "helpers/position"; 5 | @import "helpers/visually-hidden"; 6 | @import "helpers/stretched-link"; 7 | @import "helpers/text-truncation"; 8 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_images.scss: -------------------------------------------------------------------------------- 1 | // Responsive images (ensure images don't scale beyond their parents) 2 | // 3 | // This is purposefully opt-in via an explicit class rather than being the default for all ``s. 4 | // We previously tried the "images are responsive by default" approach in Bootstrap v2, 5 | // and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps) 6 | // which weren't expecting the images within themselves to be involuntarily resized. 7 | // See also https://github.com/twbs/bootstrap/issues/18178 8 | .img-fluid { 9 | @include img-fluid(); 10 | } 11 | 12 | 13 | // Image thumbnails 14 | .img-thumbnail { 15 | padding: $thumbnail-padding; 16 | background-color: $thumbnail-bg; 17 | border: $thumbnail-border-width solid $thumbnail-border-color; 18 | @include border-radius($thumbnail-border-radius); 19 | @include box-shadow($thumbnail-box-shadow); 20 | 21 | // Keep them at most 100% wide 22 | @include img-fluid(); 23 | } 24 | 25 | // 26 | // Figures 27 | // 28 | 29 | .figure { 30 | // Ensures the caption's text aligns with the image. 31 | display: inline-block; 32 | } 33 | 34 | .figure-img { 35 | margin-bottom: $spacer / 2; 36 | line-height: 1; 37 | } 38 | 39 | .figure-caption { 40 | @include font-size($figure-caption-font-size); 41 | color: $figure-caption-color; 42 | } 43 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Toggles 2 | // 3 | // Used in conjunction with global variables to enable certain theme features. 4 | 5 | // Vendor 6 | @import "vendor/rfs"; 7 | 8 | // Deprecate 9 | @import "mixins/deprecate"; 10 | 11 | // Helpers 12 | @import "mixins/breakpoints"; 13 | @import "mixins/image"; 14 | @import "mixins/resize"; 15 | @import "mixins/visually-hidden"; 16 | @import "mixins/reset-text"; 17 | @import "mixins/text-truncate"; 18 | 19 | // Utilities 20 | @import "mixins/utilities"; 21 | 22 | // Components 23 | @import "mixins/alert"; 24 | @import "mixins/buttons"; 25 | @import "mixins/caret"; 26 | @import "mixins/pagination"; 27 | @import "mixins/lists"; 28 | @import "mixins/list-group"; 29 | @import "mixins/forms"; 30 | @import "mixins/table-variants"; 31 | 32 | // Skins 33 | @import "mixins/border-radius"; 34 | @import "mixins/box-shadow"; 35 | @import "mixins/gradients"; 36 | @import "mixins/transition"; 37 | 38 | // Layout 39 | @import "mixins/clearfix"; 40 | @import "mixins/container"; 41 | @import "mixins/grid"; 42 | -------------------------------------------------------------------------------- /sass/bootstrap/scss/_nav.scss: -------------------------------------------------------------------------------- 1 | // Base class 2 | // 3 | // Kickstart any navigation component with a set of style resets. Works with 4 | // `