diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml new file mode 100644 index 00000000..e427ab22 --- /dev/null +++ b/.github/workflows/generate-docs.yml @@ -0,0 +1,52 @@ +name: Generate and Deploy Documentation + +on: + push: + branches: + - main + +permissions: + actions: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + publish-docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 9.x + + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Add DocFX to PATH + run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + + - name: Generate documentation + run: | + cd docs + docfx metadata + docfx build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: '_site' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 29b79576..3a15ae7b 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,45 @@ ![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) # WebExpress -WebExpress is a lightweight web server optimized for use in low-performance environments (e.g. Rasperry PI). By providing +`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Rasperry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net -language (e.g. C#). Some advantages of WebExpress are: +language (e.g. C#). Some advantages of `WebExpress` are: - It is easy to use. - It offers a variety of features and tools that can help you build and manage your website. - It is fast and efficient and can help you save time and money. - It is flexible and can be customized to meet your specific requirements. -The WebExpress family includes the following projects: +The `WebExpress` family includes the following projects: -- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for WebExpress applications and the documentation. -- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for WebExpress applications. -- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for WebExpress applications. -- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for WebExpress applications. -- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for WebExpress applications. +- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for `WebExpress` applications and the documentation. +- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for `WebExpress` applications. +- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for `WebExpress` applications. +- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for `WebExpress` applications. +- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for `WebExpress` applications. # WebExpress.WebUI -WebExpress.WebUI is part of the WebExpress family. It provides templates and controls that standardize and facilitate the -creation of web pages. - -# Libraries used -- https://github.com/dotnet/core (MIT) -- https://getbootstrap.com/ (MIT) -- https://www.chartjs.org (MIT) -- https://jquery.com/ (MIT) -- https://summernote.org/ (MIT) -- https://popper.js.org/ (MIT) -- https://github.com/kurtobando/simple-tags (MIT) -- https://github.com/uxsolutions/bootstrap-datepicker (Apache 2.0) -- https://github.com/xoofx/markdig (BSD-2-Clause license) +`WebExpress.WebUI` is part of the WebExpress family. It provides templates and controls that standardize and facilitate the creation of web pages. # Download The current binaries are available for download [here](https://github.com/ReneSchwarzer/WebExpress/releases). # Start -To get started with WebExpress, use the following links and tutorials. +If you're looking to get started with `WebExpress`, we would recommend using the following documentation. It can help you understand the platform. -- [installation guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) -- [development guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) +- [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) +- [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) +- [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) +- [WebExpress.WebUI API Documentation](https://reneschwarzer.github.io/WebExpress.WebUI/) +- [WebExpress.WebApp API Documentation](https://reneschwarzer.github.io/WebExpress.WebApp/) +- [WebExpress.WebIndex API Documentation](https://reneschwarzer.github.io/WebExpress.WebIndex/) + +# Learning +The following tutorials illustrate the essential techniques of `WebExpress`. These tutorials are designed to assist you, as a developer, in understanding the various aspects of `WebExpress`. Each tutorial provides a detailed, step-by-step guide that you can work through using an example. If you’re interested in beginning the development of `WebExpress` components, we would recommend you to complete some of these tutorials. -## Tutorials - [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) +- [WebApp](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebApp#readme) +- [WebIndex](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebIndex#readme) # Tags -#NETCore #WebExpress #Controls #Templates +#WebUI #WebExpress #DotNet #NETCore diff --git a/docs/api/toc.yml b/docs/api/toc.yml new file mode 100644 index 00000000..20c32afa --- /dev/null +++ b/docs/api/toc.yml @@ -0,0 +1,2 @@ +### YamlMime:TableOfContent +[] diff --git a/docs/assets/webexpress.ico b/docs/assets/webexpress.ico new file mode 100644 index 00000000..c0909c98 Binary files /dev/null and b/docs/assets/webexpress.ico differ diff --git a/docs/assets/webexpress.svg b/docs/assets/webexpress.svg new file mode 100644 index 00000000..e9243f58 --- /dev/null +++ b/docs/assets/webexpress.svg @@ -0,0 +1,40 @@ + + + + + + + + + + template + + + Tabelle.2 + + + + Tabelle.35 + + + + diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 00000000..822538c8 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + { + "src": [ + { + "files": [ + "src/WebExpress.WebUI/*.csproj" + ], + "src": "../" + } + ], + "dest": "api", + "outputFormat": "apiPage" + } + ], + "build": { + "content": [ + { + "files": [ "**/*.{md,yml}" ], + "exclude": [ "_site/**", "obj/**" ] + } + ], + "resource": [ + { + "files": [ "**/images/**", "**/media/**", "codesnippet/**" ], + "exclude": [ "_site/**", "obj/**" ] + }, + { + "files": [ "assets/webexpress.ico", "assets/webexpress.svg" ] + }, + { + "src": "../schemas", + "files": [ "**/*.json" ], + "dest": "schemas" + } + ], + "postProcessors": [ "ExtractSearchIndex" ], + "globalMetadata": { + "_appTitle": "WebExpress.WebUI", + "_appName": "WebExpress.WebUI", + "_appFaviconPath": "assets/webexpress.ico", + "_appLogoPath": "assets/webexpress.svg", + "pdf": false + }, + "markdownEngineProperties": { + "alerts": { + "TODO": "alert alert-secondary" + } + }, + "xref": [ + "../.xrefmap.json" + ], + "output": "../_site", + "template": [ + "default", + "modern", + "template" + ] + } +} diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..07712048 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,30 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) + +# WebExpress + +WebExpress is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing +a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .NET +language (e.g. C#). Some advantages of WebExpress are: + +- It is easy to use. +- It offers a variety of features and tools that can help you build and manage your website. +- It is fast and efficient and can help you save time and money. +- It is flexible and can be customized to meet your specific requirements. + +The WebExpress family includes the following projects: + +- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for WebExpress applications and the documentation. +- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for WebExpress applications. +- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for WebExpress applications. +- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for WebExpress applications. +- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for WebExpress applications. + +# WebExpress.WebUI +`WebExpress.WebUI` is part of the WebExpress family. It provides templates and controls that standardize and facilitate the +creation of web pages. + +# Download +The current binaries are available for download [here](https://github.com/ReneSchwarzer/WebExpress/releases). + +# Tags +#WebUI #WebExpress #DotNet #NETCore diff --git a/docs/javascript_ui_api.md b/docs/javascript_ui_api.md new file mode 100644 index 00000000..a9edfd80 --- /dev/null +++ b/docs/javascript_ui_api.md @@ -0,0 +1,186 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# Introduction +`WebExpress` is a powerful framework for creating modern web applications. This documentation provides a comprehensive overview of the JavaScript UI controls, explains their significance, highlights the differences from server-side controls, and demonstrates how these components can be used not only within `WebExpress` but also in other environments. + +# JavaScript UI API +The user interface (UI) is a central component of any web application. Traditionally, server-side controls managed rendering and business logic on the server. With the increasing demand for real-time interactions and dynamic user experiences, the use of JavaScript-based UI elements has grown. `WebExpress` provides the foundation for effectively integrating with JavaScript-based UI elements, enabling developers to create smoother, more responsive applications. + +JavaScript UI elements have become indispensable in modern web development as they significantly enhance the user experience. By directly processing updates and interactions in the browser, these elements offer: + +- **Smooth, interactive experiences:** Users benefit from faster, more dynamic interactions, similar to desktop applications. +- **Reduced server load:** With client-side logic, the need for constant server communication is reduced. + +A major advantage of these JavaScript UI elements is that they are not limited to `WebExpress`. They can be integrated into standalone web applications or used in combination with other frameworks such as `Angular`, `React`, or `Vue.js`. This versatility opens up a wide range of possibilities, allowing the enhancement of any project's user interface with modern, responsive UI elements. + +The goal of `WebExpress` is to create a robust, scalable, and efficient system for dynamically linking and interacting with UI elements. Emphasis is placed on modularity, performance, accessibility, and extensibility. The JavaScript classes should be decoupled from the HTML code as much as possible. This creates a clean, maintainable, and modular architecture. The separation helps manage logic entirely in JavaScript while using HTML solely for structure and semantic presentation of content. + +- **Separation of logic and presentation**: The HTML code should remain purely semantic and contain no references to the specific implementation in JavaScript. +- **Modularity and scalability**: JavaScript classes should be extendable and maintainable independently of the HTML. +- **Central control**: Introduction of a central controller that handles the initialization and management of containers and instances. +- **Flexibility**: Support for dynamic changes to the DOM, such as adding containers at runtime without modifying the HTML code. +- **Clarity**: Use of an abstracted data structure to configure containers via JavaScript. + +# Architecture Overview + +The development of modern web applications requires effective and efficient handling of user interfaces and their interactions. `WebExpress` enables the creation of dynamic and responsive web applications. The following is a closer look at the architecture of the JavaScript UI in `WebExpress`, with a focus on the modularity of the components: + +``` +β•”WebExpress.WebUi══════════════════════════════════════════════════════════════════════╗ +β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•‘ +β•‘ β”‚ Controller β”‚ β•‘ +β•‘ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β•‘ +β•‘ β”‚ - Manages Containersβ”‚ β•‘ +β•‘ β”‚ - Monitors DOM β”‚ β•‘ +β•‘ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β•‘ +β•‘ β”‚ β•‘ +β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•‘ +β•‘ β”‚ β”‚ β•‘ +β•‘ v v β•‘ +β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•‘ +β•‘ β”‚ Container 1 β”‚ β”‚ Container 2 β”‚ β•‘ +β•‘ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β•‘ +β•‘ β”‚ - HTML Element β”‚ β”‚ - HTML Element β”‚ β•‘ +β•‘ β”‚ - Semantic Tags β”‚ β”‚ - Semantic Tags β”‚ β•‘ +β•‘ β”‚ - Data Attributes β”‚ β”‚ - Data Attributes β”‚ β•‘ +β•‘ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β•‘ +β•‘ β”‚ β”‚ β•‘ +β•‘ v v β•‘ +β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•‘ +β•‘ β”‚ Instance 1 β”‚ β”‚ Instance 2 β”‚ β•‘ +β•‘ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β•‘ +β•‘ β”‚ - JavaScript Class β”‚ β”‚ - JavaScript Class β”‚ β•‘ +β•‘ β”‚ - Handles Logic β”‚ β”‚ - Handles Logic β”‚ β•‘ +β•‘ β”‚ - Interacts with DOMβ”‚ β”‚ - Interacts with DOMβ”‚ β•‘ +β•‘ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β•‘ +β•‘ β”‚ β”‚ β•‘ +β•‘ v v β•‘ +β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•‘ +β•‘ β”‚ jquery event system β”‚ β•‘ +β•‘ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• +``` + +The controller module is a central component for managing all containers and their associated instances. It registers containers during initialization and detects dynamic changes, for example through the use of the `MutationObserver`. + +Containers are defined in the HTML DOM structure and implemented with semantic HTML such as `section` or `article` instead of `div`. This structure is provided by the server-side controls in `WebExpress`. Data attributes like data-* can be used to store specific configurations, and accessibility is ensured through aria-* attributes. + +Each JavaScript UI instance represents the logic and behavior of a container and includes methods for interactions. The instances are separated from the presentation, enabling a clear separation of logic and DOM. + +The jQuery event system functions in the context of WebExpress as a lightweight pub/sub model that efficiently enables message passing between different containers and instances. By utilizing jQuery events, communication is optimized, direct dependencies are minimized, and debugging is simplified. A concrete example from WebExpress: when a form is submitted in Container 1, a custom event can be triggered using jQuery to signal Container 2 to update its display accordingly. This decoupling of components promotes a loosely coupled architecture, where modules can operate independently. + +# Example Code +The following example demonstrates how the separation of logic and presentation, the modularity of JavaScript classes, and the central control by the controller are implemented in `WebExpress`. First, the HTML structure is defined: + +```html + + + + + + WebExpress Example + + + + +
+

Container 1

+ +
+
+

Container 2

+

+
+ + +``` + +Next follows the JavaScript logic for managing and interacting with the containers: + +```javascript +$(document).ready(function() { + const controller = new Controller(); + + // Container 1 Logic + class Container1 { + constructor(element) { + this.element = $(element); + this.button = this.element.find('#button1'); + this.init(); + } + + init() { + this.button.on('click', () => { + $(document).trigger('buttonClicked', ['Button in Container 1 was clicked']); + }); + } + } + + // Container 2 Logic + class Container2 { + constructor(element) { + this.element = $(element); + this.message = this.element.find('#message'); + this.init(); + } + + init() { + $(document).on('buttonClicked', function(event, data) { + this.message.text(data); + });); + } + } + + // Controller to manage containers + class Controller { + constructor() { + this.containers = []; + this.init(); + this.observeMutations(); + } + + init() { + this.registerContainers(); + } + + // Registers containers based on CSS classes + registerContainers() { + $('.webexpress-container').each((index, section) => { + if ($(section).hasClass('container1')) { + this.containers.push(new Container1(section)); + } else if ($(section).hasClass('container2')) { + this.containers.push(new Container2(section)); + } else { + console.warn(`Unknown container class: ${section.className}`); + } + }); + } + + // Observes DOM mutations to dynamically register new containers + observeMutations() { + const observer = new MutationObserver(mutations => { + mutations.forEach(mutation => { + if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { + this.registerContainers(); + } + }); + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + } + } + + // Initialize the Controller + controller.init(); +}); +``` + +In this simple example, the architecture of `WebExpress` is demonstrated by creating two containers (Container1 and Container2), each with its own logic, and communicating via the jquery event system. The `Controller` manages the initialization and registration of the containers. + +`Container1` contains a button that triggers an event (`buttonClicked`) on the jquery event system when clicked. +`Container2` listens for the `buttonClicked` event and updates the text content of a paragraph to display the message. + +This example shows how the separation of logic and presentation, the modularity of JavaScript classes, and the central control by the controller are implemented in `WebExpress`. \ No newline at end of file diff --git a/docs/jsui-api/buttonCtrl.md b/docs/jsui-api/buttonCtrl.md new file mode 100644 index 00000000..2213770d --- /dev/null +++ b/docs/jsui-api/buttonCtrl.md @@ -0,0 +1,48 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# ButtonCtrl +The `ButtonCtrl` control is a simple button that offers advanced features like icons and text. It allows the creation of dynamic and interactive buttons. This control can be configured with a variety of settings to meet the specific requirements of a web application. + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ icon label β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Settings +The control is configured using a `settings` object that supports the following properties: + +|Setting |Type |Description +|---------|---------|----------------------------------------------- +|id |string |The ID of the control. +|css |string |The CSS classes for styling the control. +|label |string |The text of the control. +|icon |string |The icon class of the control. +|color |string |The color of the control. +|disabled |boolean |Specifies whether the control is disabled. +|url |string |The URL the control links to. +|onclick |function |The click event function for the control. + +## Example Code +The following code demonstrates how to create a simple button with various settings: + +javascript +```javascript +const settings = { + id: "simpleButton", + label: "Click Me", + color: "btn-warning", + icon: "fas fa-hand-pointer" +}; + +const buttonCtrl = new webexpress.webui.buttonCtrl(settings); + +$("body").append(buttonCtrl.getCtrl); +``` + +In this example, a button is created that includes an icon (`fas fa-hand-pointer`) and the text "Click Me". The control itself is configured using the `settings` object, which defines the ID, CSS classes, and text for the control. + +## Events +The control triggers the following event: + +- webexpress.webui.click: This event is triggered when the button is clicked. \ No newline at end of file diff --git a/docs/jsui-api/dropDownButtonCtrl.md b/docs/jsui-api/dropDownButtonCtrl.md new file mode 100644 index 00000000..18f12974 --- /dev/null +++ b/docs/jsui-api/dropDownButtonCtrl.md @@ -0,0 +1,74 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# DropDownButtonCtrl +The `DropDownButtonCtrl` control is a button with a dropdown menu that offers advanced features such as links and icons. It enables the creation of dynamic and interactive menu elements. This control can be customized with a variety of settings and options to meet the specific requirements of a web application. + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ icon label β”‚ + β””β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”Œβ”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ header β”‚ + β”‚ icon label β”‚ + β”‚ icon label β”‚ + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ + β”‚ icon label β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Settings +The control is defined through a `settings` object that contains the following properties: + +|Setting |Typ |Description +|--------|-------|----------------------------------------------- +|id |string |The ID of the control. +|css |string |The CSS classes for styling the control. +|menucss |string |A CSS class for styling the popup menu. +|label |string |The text of the control. +|icon |string |The icon class of the control. + +## Options +The menu items are defined as an array of `option` objects with the following properties: + +|Property |Type |Description +|------------|---------|------------------------------- +|css |string |The CSS class for the menu item. +|icon |string |The icon class for the menu item. +|color |string |The color of the menu item. +|label |string |The text of the menu item. +|url |string |The URL of the menu item. +|disabled |boolean |Indicates whether the menu item is disabled. +|onclick |Function |The click event function for the menu item. + +## Example Code +The following code demonstrates how to create a dropdown button with various options and settings: + +```javascript +const options = [ + { label: "Settings", css: "dropdown-header" }, + { label: "Home", url: "#home", icon: "fas fa-home", color: "text-primary", onclick: "console.log('Home clicked')" }, + { label: "Profile", url: "#profile", icon: "fas fa-user", color: "text-success" }, + { css: "dropdown-divider" }, + { label: "Logout", url: "#logout", icon: "fas fa-sign-out-alt", color: "text-danger", disabled: true } +]; + +const settings = { + id: "mainDropdown", + css: "dropdown-container", + menucss: "menu-style", + label: "Actions", + icon: "fas fa-ellipsis-v" +}; + +const dropDownButtonCtrl = new webexpress.webui.dropDownButtonCtrl(options, settings); + +$("body").append(dropDownButtonCtrl.getCtrl); +``` + +In this example, a `DropDownButtonCtrl` is created containing multiple menu items, including "Home," "Profile," "Logout," and a divider. Each menu item can be configured individually, including assigning icons, colors, and click event functions. + +## Events +This control fires these events: + +- webexpress.webui.change.value: This event is triggered when the selected value changes. The following parameters are passed: + - Parameter value: The ID of the selected option. \ No newline at end of file diff --git a/docs/jsui-api/expandCtrl.md b/docs/jsui-api/expandCtrl.md new file mode 100644 index 00000000..338e0d10 --- /dev/null +++ b/docs/jsui-api/expandCtrl.md @@ -0,0 +1,55 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# ExpandCtrl +The `ExpandCtrl` control is a container used for showing and hiding content. It allows the creation of expandable sections that can be toggled by clicking on a header or icon. The control triggers events to indicate visibility changes. + +``` + β”Œ---------------┐ + Β¦ β–Ό Header Text Β¦ + β””---------------β”˜ + β”Œ--------------------------------┐ + Β¦ Hidden Content Β¦ + β””--------------------------------β”˜ +``` + +## Settings +The control is configured using a `settings` object that supports the following properties: + +|Setting |Type |Description +|-----------|----------|----------------------------------------------------- +| id | string | The ID of the control. +| css | string | The CSS classes for styling the control. +| header | string | The text for the header. +| headerCss | string | The CSS classes for styling the header. + +## Example Code +The following code demonstrates how to create an expandable container with various settings: + +```javascript +
+ ... +
+... + +const ctrl1 = new webexpress.webui.expandCtrl({ + id: "c174f5ab-bba0-480e-a29c-2f8b7b87a2da", + header: "More Options", + css: "" +}); + +ctrl1.content = $("#container").children(); + +ctrl1.on('webexpress.webui.change.visibility', function (key) { + console.log("Expand: " + key); +}); + +parent.append(ctrl1.getCtrl); +``` + +In this example, an expandable container with a header is created. The container can be toggled to show or hide its content by clicking the header or icon. + +## Events +The control triggers the following events: + +- webexpress.webui.change.visibility: This event is triggered when the visibility of the content changes. The following parameters are passed: + - Parameter isVisible: A boolean value indicating whether the content is visible (true) or not (false). \ No newline at end of file diff --git a/docs/jsui-api/moveCtrl.md b/docs/jsui-api/moveCtrl.md new file mode 100644 index 00000000..7a40cde2 --- /dev/null +++ b/docs/jsui-api/moveCtrl.md @@ -0,0 +1,54 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# MoveCtrl +The `MoveCtrl` control provides a selection box to choose and move options between two lists. It triggers events to signal changes in the selected values. + +## Settings +The control is configured using a `settings` object that supports the following properties: + +|Setting |Type |Description +|--------|-------|------------------------------------------------ +|id |string |The ID of the control. +|name |string |The name of the hidden input field. +|css |string |The CSS classes for styling the control. +|header |object |The headers { selected, available }. +|buttons |object |The button labels { toselectedall, toselected, toavailable, toavailableall }. + +## Example Code +The following code demonstrates how to create a moveCtrl control with various settings: + +```javascript +const settings = { + id: "moveCtrl", + name: "move", + header: { + selected: "Selected", + available: "Available" + }, + buttons: { + toselectedall: "Select All", + toselected: "Select", + toavailable: "Available", + toavailableall: "Make All Available" + } +}; + +const moveCtrl = new webexpress.webui.moveCtrl(settings); + +moveCtrl.options = [ + { Id: 1, label: "Option 1", icon: "fas fa-check", image: "path/to/image1.jpg" }, + { Id: 2, label: "Option 2", icon: "fas fa-check", image: "path/to/image2.jpg" }, + { Id: 3, label: "Option 3", icon: "fas fa-check", image: "path/to/image3.jpg" } +]; + +$("body").append(moveCtrl.getCtrl); +``` + +In this example, a selection box is created with an ID and CSS classes. Multiple options are added that can be moved between the "Selected" and "Available" lists. Users can move options via drag-and-drop or by clicking the buttons. + +## Events +This control fires these events: + +|Event |Callback Signature |Description +|------------------------------|---------------------------|------------------------------------ +|webexpress.webui.change.value |function(value: any): void |This event is triggered when the selected values change. The value parameter contains the newly selected values. diff --git a/docs/jsui-api/searchCtrl.md b/docs/jsui-api/searchCtrl.md new file mode 100644 index 00000000..3bdeae7f --- /dev/null +++ b/docs/jsui-api/searchCtrl.md @@ -0,0 +1,45 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# SearchCtrl +The `SearchCtrl` control is an input field where search commands can be entered. It triggers events to signal changes in the filter and enables easy clearing of the search field using an icon. + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ πŸ” Placeholder Text βœ– β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Settings +The control is configured using a `settings` object that supports the following properties: + +|Setting |Type |Description +|-------------|----------|-------------------------------------------------- +| id | string | The ID of the control. +| css | string | The CSS classes for styling the control. +| placeholder | string | The placeholder text for the input field. +| icon | string | The icon class for the search symbol. + +## Example Code +The following code demonstrates how to create a search field with various settings: + +```javascript +const settings = { + id: "searchField", + css: "search-container", + placeholder: "Suche...", + icon: "fas fa-search" +}; + +const searchCtrl = new webexpress.webui.searchCtrl(settings); + +$("body").append(searchCtrl.getCtrl); +``` + +In this example, a search field is created with a placeholder text and a search icon. The user can clear the search field by clicking on the "X" icon. + +## Events +This control fires these events: + +|Event |Callback Signature |Description +|-------------------------------|-------------------------------|------------------------------------ +|webexpress.webui.change.filter |function(filter: string): void |Triggered on Keyup: Every time the user types into the search field (keyup event), this event is fired with the current value of the input field passed as the filter parameter. This allows developers to update search results or filter data dynamically.

Triggered on Clear: When the clear button (the "X" icon) is clicked, the search field is reset to an empty string, and the event is fired with an empty filter. This clearly signifies that the search criteria have been cleared. diff --git a/docs/jsui-api/selectionCtrl.md b/docs/jsui-api/selectionCtrl.md new file mode 100644 index 00000000..0781a65f --- /dev/null +++ b/docs/jsui-api/selectionCtrl.md @@ -0,0 +1,47 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# SelectionCtrl +The `SelectionCtrl` control is a selection box that allows one or multiple options to be chosen from a dropdown list. It supports filtering options and displaying additional information for each option. The control triggers events to signal changes in the filter and selected values. + +## Settings +The configuration of the control is managed through a `settings` object that includes the following attributes: + +|Setting |Type |Description +|-----------------|----------|--------------------------------------------------- +| id | string | The ID of the control. +| name | string | The name of the hidden input field. +| css | string | The CSS classes for styling the control. +| placeholder | string | The placeholder text for the filter input field. +| hidedescription | boolean | Disables the description of the options. +| multiselect | boolean | Enables the selection of multiple options. + +## Example Code +The following code demonstrates how to create a selection box with various settings: + +```javascript +const settings = { + id: "selectionField", + name: "selection", + placeholder: "WΓ€hle eine Option...", + hidedescription: false, + multiselect: true +}; + +const selectionCtrl = new webexpress.webui.selectionCtrl(settings); + +selectionCtrl.options = [ + { id: 1, label: "Option 1", description: "Beschreibung von Option 1", image: "path/to/image1.jpg", color: "text-danger" }, + { id: 2, label: "Option 2", description: "Beschreibung von Option 2", image: "path/to/image2.jpg", color: "text-success" }, + { id: 3, label: "Option 3", description: "Beschreibung von Option 3", image: "path/to/image3.jpg", color: "text-warning" } +]; + +$("body").append(selectionCtrl.getCtrl); +``` + +In this example, a selection box is created with a placeholder text and multiple choices. The user can select multiple options and clear the selection box by clicking the "X" icon. + +##Events +The control triggers the following events: + +- webexpress.webui.change.filter: This event is triggered when the search filter changes. The parameter filter contains the new search term. + Parameter filter: The new search term entered into the search field. \ No newline at end of file diff --git a/docs/jsui-api/treeCtrl.md b/docs/jsui-api/treeCtrl.md new file mode 100644 index 00000000..55bd01bd --- /dev/null +++ b/docs/jsui-api/treeCtrl.md @@ -0,0 +1,58 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress.Doc/main/assets/banner.png) + +# TreeCtrl +The `TreeCtrl` control is a tree component that enables the hierarchical display of data. It supports the dynamic expanding and collapsing of nodes as well as the display of additional information for each node. The control triggers events to signal changes in visibility. + +``` + β”Œ--------------------------------┐ + Β¦ β–Ό Node 1 Β¦ + Β¦ β–Ί Node 1.1 Β¦ + Β¦ β–Ό Node 1.2 Β¦ + Β¦ ● Node 1.2.1 Β¦ + Β¦ ● Node 2 Β¦ + β””--------------------------------β”˜ +``` + +## Settings +The control is configured using a `settings` object that supports the following properties: + +|Setting |Type |Description +|---------|--------|----------------------------------------------------- +| id | string | The ID of the control. +| css | string | The CSS classes for styling the control. + +## Example Code +The following code demonstrates how to create a tree control with various settings: + +```javascript +const settings = { + id: "treeCtrl" +}; + +const treeCtrl = new webexpress.webui.treeCtrl(settings); + +treeCtrl.nodes = [ + { id: 1, label: "Node 1", children: [ + { id: 2, label: "Node 1.1" }, + { id: 3, label: "Node 1.2", children: [ + { id: 4, label: "Node 1.2.1" } + ]} + ]}, + { id: 5, label: "Node 2" } +]; + +treeCtrl.on('webexpress.webui.change.visibility', function (li, node) { + console.log("Visibility changed for node:", node.label); +}); + +$("body").append(treeCtrl.getCtrl); +``` + +In this example, a tree control is created with an ID and CSS classes. Several nodes are added in a hierarchical structure. The user can expand or collapse nodes to display or hide their child nodes. + +## Events +The control triggers the following events: + +- webexpress.webui.change.visibility: This event is triggered when the visibility of a node is changed. The following parameters are passed: + - Parameter li: The list element (li) representing the node. + - Parameter node: The node object containing the node's data. diff --git a/docs/template/dashboard.html.tmpl b/docs/template/dashboard.html.tmpl new file mode 100644 index 00000000..0288fe86 --- /dev/null +++ b/docs/template/dashboard.html.tmpl @@ -0,0 +1,47 @@ +{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}} +{{!master(layout/_master.tmpl)}} +

{{title}}

+{{#items.Length}} +
+{{#items}} +
+ {{name}} +
+
{{name}}
+

{{{description}}}

+
+
+ {{#usage}} +
+ {{#config}}
docfx.json: {{config}}
{{/config}} + {{#command}}
docfx: {{command}}
{{/command}} + {{#init}}
docfx init: {{init}}
{{/init}} +
+ {{/usage}} +
+
+{{/items}} +
+{{/items.Length}} + + diff --git a/docs/template/public/main.css b/docs/template/public/main.css new file mode 100644 index 00000000..b0b8c1fd --- /dev/null +++ b/docs/template/public/main.css @@ -0,0 +1,10 @@ +/** + * Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + */ + +/* Checkout https://getbootstrap.com/docs/5.3/customize/color/ for more customization options */ +body { + --bs-link-color-rgb: 66, 184, 131 !important; + --bs-link-hover-color-rgb: 64, 180, 128 !important; +} diff --git a/docs/template/public/main.js b/docs/template/public/main.js new file mode 100644 index 00000000..d5c4867a --- /dev/null +++ b/docs/template/public/main.js @@ -0,0 +1,14 @@ +/** + * Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + */ + +export default { + iconLinks: [ + { + icon: 'github', + href: 'https://github.com/dotnet/docfx', + title: 'GitHub' + } + ] +} diff --git a/docs/template/schemas/Dashboard.schema.json b/docs/template/schemas/Dashboard.schema.json new file mode 100644 index 00000000..a00ab759 --- /dev/null +++ b/docs/template/schemas/Dashboard.schema.json @@ -0,0 +1,45 @@ +{ + "title": "Dashboard", + "$schema": "https://dotnet.github.io/docfx/schemas/v1.0/schema.json#", + "version": "1.0.0", + "description": "Schema for dashboard", + "id": "https://github.com/dotnet/docfx/schemas/Dashboard.schema.json", + "type": "object", + "properties": { + "uid": { + "type": "string", + "contentType": "uid" + }, + "title": { + "type": "string", + "tags": [ + "localizable" + ] + }, + "description": { + "type": "string", + "contentType": "markdown", + "tags": [ + "localizable" + ] + }, + "items": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string", + "contentType": "markdown", + "tags": [ + "localizable" + ] + } + }, + "type": "object" + }, + "type": "array" + } + } +} \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 00000000..60141521 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,25 @@ +ο»Ώ- name: Home + href: index.md +- name: User Guide + href: user-guide.md +- name: API Documentation + href: api/WebExpress.WebUI.html +- name: JavaScript UI API Documentation + href: javascript_ui_api.md + children: + - name: ButtonCtrl + href: jsui-api/buttonCtrl.md + - name: DropDownButtonCtrl + href: jsui-api/dropDownButtonCtrl.md + - name: ExpandCtrl + href: jsui-api/expandCtrl.md + - name: MoveCtrl + href: jsui-api/moveCtrl.md + - name: SearchCtrl + href: jsui-api/searchCtrl.md + - name: SelectionCtrl + href: jsui-api/selectionCtrl.md + - name: TreeCtrl + href: jsui-api/treeCtrl.md +- name: Tutorials + href: tutorials.md diff --git a/docs/tutorials.md b/docs/tutorials.md new file mode 100644 index 00000000..14d79d3a --- /dev/null +++ b/docs/tutorials.md @@ -0,0 +1,18 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) + +# Tutorials +Welcome to the `WebExpress` Tutorials! Here, you'll find step-by-step guides and helpful resources to get the most out +of `WebExpress`. Whether you're a beginner just starting out or an experienced developer looking to expand your skills, +our tutorials offer something for everyone. + +# Getting Started +Begin with our basic tutorial: + +- [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) +- [WebApp](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebApp#readme) +- [WebIndex](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebIndex#readme) + +This tutorial will guide you through the initial steps of creating and running your first `WebExpress` application. + +Stay tuned for more exciting and educational tutorials to help you unlock the full potential of `WebExpress`. Happy coding and +best of luck with your projects! diff --git a/docs/user-guide.md b/docs/user-guide.md new file mode 100644 index 00000000..b6f245d3 --- /dev/null +++ b/docs/user-guide.md @@ -0,0 +1,17 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) + +# User guide +Welcome to the `WebExpress.WebUI` User Guide. This guide will help you get started with `WebExpress.WebUI` and make the most out of its +features. Follow the links below to begin your journey. + +# Getting started +To get started with `WebExpress.WebUI`, use the following guides: + +- [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) +- [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) +- [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) +- [WebExpress.WebUI API Documentation](https://reneschwarzer.github.io/WebExpress.WebUI/) +- [WebExpress.WebApp API Documentation](https://reneschwarzer.github.io/WebExpress.WebApp/) +- [WebExpress.WebIndex API Documentation](https://reneschwarzer.github.io/WebExpress.WebIndex/) + +We hope you enjoy using `WebExpress.WebUI` and find it valuable for your projects. Happy coding! diff --git a/src/WebExpress.WebUI.Test/Data/ComplexForm.txt b/src/WebExpress.WebUI.Test/Data/ComplexForm.txt new file mode 100644 index 00000000..b56a47cf --- /dev/null +++ b/src/WebExpress.WebUI.Test/Data/ComplexForm.txt @@ -0,0 +1,30 @@ +ο»Ώ
+ + +
+ +

FragmentHeaderPrimary

+
+
+
+
+ + + Help1 +
+
+ + + Help2 +
+
+
+
+ +
+ +
\ No newline at end of file diff --git a/src/WebExpress.WebUI.Test/Fixture/AssertExtensions.cs b/src/WebExpress.WebUI.Test/Fixture/AssertExtensions.cs new file mode 100644 index 00000000..9c1753d8 --- /dev/null +++ b/src/WebExpress.WebUI.Test/Fixture/AssertExtensions.cs @@ -0,0 +1,89 @@ +ο»Ώusing System.Text.RegularExpressions; +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebUI.Test.Fixture +{ + /// + /// Provides extension methods for assertions. + /// + public static partial class AssertExtensions + { + /// + /// Gets a regular expression that matches whitespace between '>' and '<' characters. + /// + /// A object that matches whitespace between '>' and '<' characters. + [GeneratedRegex(@">\s+<")] + private static partial Regex WhitespaceRegex(); + + /// + /// Asserts that the actual string is equal to the expected string, allowing for placeholders. + /// + /// The Assert instance (not used, but required for extension method). + /// The expected string with placeholders. + /// The actual string to compare. + public static void EqualWithPlaceholders(string expected, string actual) + { + var str = RemoveLineBreaks(actual?.ToString()); + Assert.True(AreEqualWithPlaceholders(expected, str), $"Expected: {expected}{Environment.NewLine}Actual: {str}"); + } + + /// + /// Asserts that the actual node is equal to the expected string, allowing for placeholders. + /// + /// The Assert instance (not used, but required for extension method). + /// The expected string with placeholders. + /// The actual string to compare. + public static void EqualWithPlaceholders(string expected, IHtmlNode actual) + { + var str = RemoveLineBreaks(actual?.ToString()); + Assert.True(AreEqualWithPlaceholders(expected, str), $"Expected: {expected}{Environment.NewLine}Actual: {str}"); + } + + /// + /// Compares two strings, allowing for placeholders in the expected string. + /// + /// The expected string, which may contain '*' as a wildcard character. + /// The actual string to compare against the expected string. + /// True if the actual string matches the expected string with placeholders; otherwise, false. + private static bool AreEqualWithPlaceholders(string expected, string actual) + { + if (expected == null && actual == null) + { + return true; + } + else if (expected != null && actual == null) + { + return false; + } + else if (expected == null && actual != null) + { + return false; + } + + var pattern = "^" + Regex.Escape(expected).Replace(@"\*", ".*") + "$"; + + return Regex.IsMatch(actual, pattern); + } + + /// + /// Removes all line breaks from the input string. + /// + /// The input string from which to remove line breaks. + /// A string with all line breaks removed. + public static string RemoveLineBreaks(string input) + { + if (string.IsNullOrEmpty(input)) + { + return input; + } + + // remove all line breaks + string result = input.Replace("\r\n", "").Replace("\r", "").Replace("\n", ""); + + // remove whitespace of any length between '>' and '<' + result = WhitespaceRegex().Replace(result, "><"); + + return result; + } + } +} diff --git a/src/WebExpress.WebUI.Test/Fixture/NonParallelTestsCollection.cs b/src/WebExpress.WebUI.Test/Fixture/NonParallelTestsCollection.cs new file mode 100644 index 00000000..67e57bc5 --- /dev/null +++ b/src/WebExpress.WebUI.Test/Fixture/NonParallelTestsCollection.cs @@ -0,0 +1,11 @@ +ο»Ώnamespace WebExpress.WebUI.Test.Fixture +{ + /// + /// Defines a collection of tests that should not be run in parallel. + /// + [CollectionDefinition("NonParallelTests", DisableParallelization = true)] + public class NonParallelTestsCollection : ICollectionFixture + { + + } +} diff --git a/src/WebExpress.WebUI.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebUI.Test/Fixture/UnitTestControlFixture.cs new file mode 100644 index 00000000..73f36bef --- /dev/null +++ b/src/WebExpress.WebUI.Test/Fixture/UnitTestControlFixture.cs @@ -0,0 +1,247 @@ +ο»Ώusing Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using System.Globalization; +using System.Net; +using System.Reflection; +using System.Text; +using WebExpress.WebCore; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.Fixture +{ + /// + /// A fixture class for unit tests, providing various mock objects and utility methods. + /// + public partial class UnitTestControlFixture : IDisposable + { + private static readonly string[] _separator = ["\r\n", "\r", "\n"]; + + /// + /// Initializes a new instance of the class and boot the component manager. + /// + public UnitTestControlFixture() + { + } + + /// + /// Create a fake server context. + /// + /// The server context. + public static IHttpServerContext CreateHttpServerContextMock() + { + return new HttpServerContext + ( + new RouteEndpoint("localhost"), + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + new RouteEndpoint("/server"), + CultureInfo.GetCultureInfo("en"), + new Log() { LogMode = LogMode.Off }, + null + ); + } + + /// + /// Create a component hub. + /// + /// The component hub. + public static ComponentHub CreateComponentHubMock() + { + var ctorComponentHub = typeof(ComponentHub).GetConstructor + ( + BindingFlags.NonPublic | BindingFlags.Instance, + null, + [typeof(HttpServerContext)], + null + ); + + var componentHub = (ComponentHub)ctorComponentHub.Invoke([CreateHttpServerContextMock()]); + + // set static field in the webex class + var type = typeof(WebEx); + var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); + + field.SetValue(null, componentHub); + + return componentHub; + } + + /// + /// Create a component hub and register the plugins. + /// + /// The component hub. + public static ComponentHub CreateAndRegisterComponentHubMock() + { + var componentHub = CreateComponentHubMock(); + var pluginManager = componentHub.PluginManager as PluginManager; + + var registerMethod = pluginManager.GetType().GetMethod("Register", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, []); + registerMethod.Invoke(pluginManager, null); + + return componentHub; + } + + /// + /// Create a fake request. + /// + /// The content of the request. + /// The URI of the request. + /// A fake request for testing. + public static Request CrerateRequestMock(string content = "", string uri = "") + { + var context = CreateHttpContextMock(content); + + var request = context.Request; + + if (!string.IsNullOrEmpty(uri)) + { + var uriProperty = typeof(Request).GetProperty("Uri", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + uriProperty.SetValue(request, new UriEndpoint(uri)); + } + + return request; + } + + /// + /// Create a fake http context. + /// + /// The content. + /// A fake http context for testing. + public static WebCore.WebMessage.HttpContext CreateHttpContextMock(string content = "") + { + var ctorRequest = typeof(Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); + var featureCollection = new FeatureCollection(); + var firstLine = content.Split('\n').FirstOrDefault(); + var lines = content.Split(_separator, StringSplitOptions.None); + var filteredLines = lines.Skip(1).TakeWhile(line => !string.IsNullOrWhiteSpace(line)); + var pos = content.Length > 0 ? content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4 : 0; + var innerContent = pos < content.Length ? content[pos..] : ""; + var contentBytes = Encoding.UTF8.GetBytes(innerContent); + + var requestFeature = new HttpRequestFeature + { + Headers = new HeaderDictionary + { + ["Host"] = "localhost", + ["Connection"] = "keep-alive", + ["ContentType"] = "text/html", + ["ContentLength"] = innerContent.Length.ToString(), + ["ContentLanguage"] = "en", + ["ContentEncoding"] = "gzip, deflate, br, zstd", + ["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + ["AcceptEncoding"] = "gzip, deflate, br, zstd", + ["AcceptLanguage"] = "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", + ["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", + ["Referer"] = "0HN50661TV8TP", + ["Cookie"] = "session=AB333C76-E73F-45E0-85FD-123320D9B85F" + }, + Body = contentBytes.Length > 0 ? new MemoryStream(contentBytes) : null, + Method = firstLine.Split(' ')?.Where(x => !string.IsNullOrEmpty(x)).FirstOrDefault() ?? "GET", + RawTarget = firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.FirstOrDefault() ?? "/", + QueryString = "?" + firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.Skip(1)?.FirstOrDefault() ?? "", + }; + + foreach (var line in filteredLines) + { + var key = line.Split(':').FirstOrDefault().Trim(); + var value = line.Split(':').Skip(1).FirstOrDefault().Trim(); + requestFeature.Headers[key] = value; + } + + requestFeature.Headers.ContentLength = contentBytes.Length; + + var requestIdentifierFeature = new HttpRequestIdentifierFeature + { + TraceIdentifier = "Ihr TraceIdentifier-Wert" + }; + + var connectionFeature = new HttpConnectionFeature + { + LocalPort = 8080, + LocalIpAddress = IPAddress.Parse("192.168.0.1"), + RemotePort = 8080, + RemoteIpAddress = IPAddress.Parse("127.0.0.1"), + ConnectionId = "0HN50661TV8TP" + }; + + featureCollection.Set(requestFeature); + featureCollection.Set(requestIdentifierFeature); + featureCollection.Set(connectionFeature); + + var context = new WebCore.WebMessage.HttpContext(featureCollection, CreateHttpServerContextMock()); + + return context; + } + + /// + /// Creates a mock render context for unit testing. + /// + /// The application context. If null, defaults to null. + /// The scopes of the page. If null, defaults to null. + /// A mock render context for testing. + public static IRenderControlContext CrerateRenderContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) + { + var request = CrerateRequestMock(); + + return new RenderControlContext(null, CreratePageContextMock(applicationContext, scopes), request); + } + + /// + /// Create a fake page context for unit testing. + /// + /// The application context. If null, defaults to null. + /// The scopes of the page. + /// A fake context for testing. + public static PageContext CreratePageContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) + { + var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); + + var pageContext = (PageContext)ctorPageContext.Invoke([]); + var applicationContextProperty = typeof(PageContext).GetProperty("ApplicationContext", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + applicationContextProperty.SetValue(pageContext, applicationContext); + + var scopesProperty = typeof(PageContext).GetProperty("Scopes", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + scopesProperty.SetValue(pageContext, scopes); + + return pageContext; + } + + /// + /// Gets the content of an embedded resource as a string. + /// + /// The name of the resource file. + /// The content of the embedded resource as a string. + public static string GetEmbeddedResource(string fileName) + { + var assembly = typeof(UnitTestControlFixture).Assembly; + var resourceName = assembly.GetManifestResourceNames() + .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); + + using var stream = assembly.GetManifestResourceStream(resourceName); + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + var data = memoryStream.ToArray(); + + return Encoding.UTF8.GetString(data); + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + } + } +} diff --git a/src/WebExpress.WebUI.Test/IHtmlNodeExtensions.cs b/src/WebExpress.WebUI.Test/IHtmlNodeExtensions.cs new file mode 100644 index 00000000..760b5704 --- /dev/null +++ b/src/WebExpress.WebUI.Test/IHtmlNodeExtensions.cs @@ -0,0 +1,32 @@ +ο»Ώusing System.Text.RegularExpressions; +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebUI.Test +{ + /// + /// Provides extension methods for the interface. + /// + internal static class IHtmlNodeExtensions + { + /// + /// Removes extra spaces and line breaks from the input node. + /// + /// The node to clean. + /// A string with all consecutive spaces and line breaks reduced to single ones. + public static string Trim(this IHtmlNode node) + { + if (node == null) + { + return string.Empty; + } + + var withoutExtraLineBreaks = Regex.Replace(node.ToString().Trim(), @"[\r\n]+", ""); + var withoutExtraSpaces = Regex.Replace(withoutExtraLineBreaks, @"\s+", " "); + var withoutSpacesBetweenBrackets = Regex.Replace(withoutExtraSpaces, @">\s+<", "><"); + var withoutSpacesLeftBrackets = Regex.Replace(withoutSpacesBetweenBrackets, @"\s+<", "<"); + var withoutSpacesRightBrackets = Regex.Replace(withoutSpacesLeftBrackets, @">\s+", ">"); + + return withoutSpacesRightBrackets; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestApplication.cs b/src/WebExpress.WebUI.Test/TestApplication.cs new file mode 100644 index 00000000..aa9b725a --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestApplication.cs @@ -0,0 +1,44 @@ +ο»Ώusing WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy application for testing purposes. + /// + [Name("TestApplication")] + [Description("application.description")] + [Icon("/assets/img/Logo.png")] + [ContextPath("/app")] + [AssetPath("/asset")] + [DataPath("/data")] + public sealed class TestApplication : IApplication + { + /// + /// Initializes a new instance of the class. + /// + /// The application context, for testing the injection. + private TestApplication(IApplicationContext applicationContext) + { + // test the injection + if (applicationContext == null) + { + throw new ArgumentNullException(nameof(applicationContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestConditionAlwaysFalse.cs b/src/WebExpress.WebUI.Test/TestConditionAlwaysFalse.cs new file mode 100644 index 00000000..b8ac0663 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestConditionAlwaysFalse.cs @@ -0,0 +1,21 @@ +ο»Ώusing WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test condition that never becomes true. + /// + public class TestConditionAlwaysFalse : ICondition + { + /// + /// Check whether the condition is fulfilled. + /// + /// The request. + /// True if the condition is fulfilled, false otherwise. + public bool Fulfillment(Request request) + { + return false; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlButtonLink.cs b/src/WebExpress.WebUI.Test/TestFragmentControlButtonLink.cs new file mode 100644 index 00000000..91e9172e --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlButtonLink.cs @@ -0,0 +1,22 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlButtonLink : FragmentControlButtonLink + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlButtonLink(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlButtonLink"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlDropdownItemLink.cs b/src/WebExpress.WebUI.Test/TestFragmentControlDropdownItemLink.cs new file mode 100644 index 00000000..5c5020b9 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlDropdownItemLink.cs @@ -0,0 +1,22 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlDropdownItemLink : FragmentControlDropdownItemLink + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlDropdownItemLink(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlDropdownItemLink"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlForm.cs b/src/WebExpress.WebUI.Test/TestFragmentControlForm.cs new file mode 100644 index 00000000..6ffeb33c --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlForm.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlForm : FragmentControlModalForm + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlForm(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Add(new ControlText() { Text = "FragmentControlForm" }); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlFragmentControlPanelTool.cs b/src/WebExpress.WebUI.Test/TestFragmentControlFragmentControlPanelTool.cs new file mode 100644 index 00000000..5c53c469 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlFragmentControlPanelTool.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlPanelTool : FragmentControlPanelTool + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlPanelTool(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Add(new ControlText() { Text = "TestFragmentControlPanelTool" }); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlImage.cs b/src/WebExpress.WebUI.Test/TestFragmentControlImage.cs new file mode 100644 index 00000000..ce154955 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlImage.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlImage : FragmentControlImage + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlImage(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Uri = new UriEndpoint("/a/b/c"); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlLink.cs b/src/WebExpress.WebUI.Test/TestFragmentControlLink.cs new file mode 100644 index 00000000..7ba35a42 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlLink.cs @@ -0,0 +1,22 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlLink : FragmentControlLink + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlLink(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlLink"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlList.cs b/src/WebExpress.WebUI.Test/TestFragmentControlList.cs new file mode 100644 index 00000000..dbda3263 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlList.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlList : FragmentControlList + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlList(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Add(new ControlListItem()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlModalForm.cs b/src/WebExpress.WebUI.Test/TestFragmentControlModalForm.cs new file mode 100644 index 00000000..65920e37 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlModalForm.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlModalForm : FragmentControlModalForm + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlModalForm(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Add(new ControlText() { Text = "FragmentControlModalForm" }); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlNavigationItemLink.cs b/src/WebExpress.WebUI.Test/TestFragmentControlNavigationItemLink.cs new file mode 100644 index 00000000..16e9c8d6 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlNavigationItemLink.cs @@ -0,0 +1,22 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlNavigationItemLink : FragmentControlNavigationItemLink + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlNavigationItemLink(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlNavigationItemLink"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlPanel.cs b/src/WebExpress.WebUI.Test/TestFragmentControlPanel.cs new file mode 100644 index 00000000..d605dcaa --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlPanel.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlPanel : FragmentControlPanel + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlPanel(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Add(new ControlText() { Text = "TestFragmentControlPanel" }); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlPanelFlexbox.cs b/src/WebExpress.WebUI.Test/TestFragmentControlPanelFlexbox.cs new file mode 100644 index 00000000..077a456b --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlPanelFlexbox.cs @@ -0,0 +1,21 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlPanelFlexbox : FragmentControlPanelFlexbox + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlPanelFlexbox(IFragmentContext fragmentContext) + : base(fragmentContext) + { + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlSplitButtonItemLink.cs b/src/WebExpress.WebUI.Test/TestFragmentControlSplitButtonItemLink.cs new file mode 100644 index 00000000..4f6a917f --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlSplitButtonItemLink.cs @@ -0,0 +1,22 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlSplitButtonItemLink : FragmentControlSplitButtonItemLink + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlSplitButtonItemLink(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlSplitButtonItemLink"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlText.cs b/src/WebExpress.WebUI.Test/TestFragmentControlText.cs new file mode 100644 index 00000000..696e6432 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlText.cs @@ -0,0 +1,24 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlText : FragmentControlText + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlText(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlText"; + Format = TypeFormatText.Paragraph; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlToolbarItemButton.cs b/src/WebExpress.WebUI.Test/TestFragmentControlToolbarItemButton.cs new file mode 100644 index 00000000..64ab5947 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlToolbarItemButton.cs @@ -0,0 +1,22 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlToolbarItemButton : FragmentControlToolbarItemButton + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlToolbarItemButton(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentControlToolbarItemButton"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentControlTree.cs b/src/WebExpress.WebUI.Test/TestFragmentControlTree.cs new file mode 100644 index 00000000..f13e3dd9 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentControlTree.cs @@ -0,0 +1,23 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + public sealed class TestFragmentControlTree : FragmentControlList + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentControlTree(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Add(new ControlListItem()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestFragmentHidden.cs b/src/WebExpress.WebUI.Test/TestFragmentHidden.cs new file mode 100644 index 00000000..83b7cb8e --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestFragmentHidden.cs @@ -0,0 +1,25 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebScope; +using WebExpress.WebUI.WebFragment; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy fragment for testing purposes. + /// + [Section()] + [Scope] + [Condition] + public sealed class TestFragmentHidden : FragmentControlText + { + /// + /// Initializes a new instance of the class. + /// + public TestFragmentHidden(IFragmentContext fragmentContext) + : base(fragmentContext) + { + Text = "TestFragmentHidden"; + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestPage.cs b/src/WebExpress.WebUI.Test/TestPage.cs new file mode 100644 index 00000000..2684afaf --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestPage.cs @@ -0,0 +1,62 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:pagea.label")] + public sealed class TestPage : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public TestPage(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree control to be processed. + public void Process(IRenderContext renderContext, VisualTreeControl visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + + visualTree.AddContent(new ControlText() { Text = "Hello World" }); + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestPlugin.cs b/src/WebExpress.WebUI.Test/TestPlugin.cs new file mode 100644 index 00000000..dd582bca --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestPlugin.cs @@ -0,0 +1,38 @@ +ο»Ώusing WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebUI.Test +{ + /// + /// A dummy plugin for testing purposes. + /// + [Name("TestPlugin")] + [Description("plugin.description")] + [Icon("/assets/img/Logo.png")] + [Application()] + public sealed class TestPlugin : IPlugin + { + /// + /// Initializes a new instance of the class. + /// + /// The plugin context. + private TestPlugin(IPluginContext pluginContext) + { + + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlButtonLink.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlButtonLink.cs new file mode 100644 index 00000000..2fd5326b --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlButtonLink.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlButtonLink : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlDropdownItemLink.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlDropdownItemLink.cs new file mode 100644 index 00000000..01d18346 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlDropdownItemLink.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlDropdownItemLink : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlForm.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlForm.cs new file mode 100644 index 00000000..c57d42fa --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlForm.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlForm : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlImage.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlImage.cs new file mode 100644 index 00000000..878e9214 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlImage.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlImage : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlLink.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlLink.cs new file mode 100644 index 00000000..09b9f36a --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlLink.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlLink : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlList.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlList.cs new file mode 100644 index 00000000..66212ecf --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlList.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlList : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlModalForm.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlModalForm.cs new file mode 100644 index 00000000..d1a31fde --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlModalForm.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlModalForm : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlNavigationItemLink.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlNavigationItemLink.cs new file mode 100644 index 00000000..8dc4ca50 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlNavigationItemLink.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlNavigationItemLink : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanel.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanel.cs new file mode 100644 index 00000000..e4eb6c83 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanel.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlPanel : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanelFlexbox.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanelFlexbox.cs new file mode 100644 index 00000000..945f4af9 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanelFlexbox.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlPanelFlexbox : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanelTool.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanelTool.cs new file mode 100644 index 00000000..555ff63c --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlPanelTool.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlPanelTool : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlSplitButtonItemLink.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlSplitButtonItemLink.cs new file mode 100644 index 00000000..b6563015 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlSplitButtonItemLink.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlSplitButtonItemLink : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlText.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlText.cs new file mode 100644 index 00000000..2190f5a8 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlText.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlText : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlToolbarItemButton.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlToolbarItemButton.cs new file mode 100644 index 00000000..2f833224 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlToolbarItemButton.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlToolbarItemButton : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentControlTree.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentControlTree.cs new file mode 100644 index 00000000..0a9e814f --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentControlTree.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentControlTree : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionFragmentHidden.cs b/src/WebExpress.WebUI.Test/TestSectionFragmentHidden.cs new file mode 100644 index 00000000..bde86234 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionFragmentHidden.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionFragmentHidden : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/TestSectionK.cs b/src/WebExpress.WebUI.Test/TestSectionK.cs new file mode 100644 index 00000000..59b53ca0 --- /dev/null +++ b/src/WebExpress.WebUI.Test/TestSectionK.cs @@ -0,0 +1,11 @@ +ο»Ώusing WebExpress.WebCore.WebSection; + +namespace WebExpress.WebUI.Test +{ + /// + /// Represents a test section for testing in the web UI. + /// + internal class TestSectionK : ISection + { + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAlert.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAlert.cs new file mode 100644 index 00000000..3f863a4d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAlert.cs @@ -0,0 +1,58 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the alert control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlAlert + { + /// + /// Tests the id property of the alert control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("id", @"
")] + [InlineData("03C6031F-04A9-451F-B817-EBD6D32F8B0C", @"
")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAlert(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the text property of the alert control. + /// + [Theory] + [InlineData(null, @"
")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAlert() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAttribute.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAttribute.cs new file mode 100644 index 00000000..1438a050 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAttribute.cs @@ -0,0 +1,161 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the attribute control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlAttribute + { + /// + /// Tests the id property of the attribute control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("id", @"
")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title property of the attribute control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("abc", @"
abc
")] + [InlineData("webexpress.webui:plugin.name", @"
WebExpress.WebUI
")] + public void Name(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + Name = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the attribute control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData(typeof(IconStar), @"
")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name color property of the attribute control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
")] + [InlineData(TypeColorText.Primary, @"
")] + [InlineData(TypeColorText.Secondary, @"
")] + [InlineData(TypeColorText.Info, @"
")] + [InlineData(TypeColorText.Success, @"
")] + [InlineData(TypeColorText.Warning, @"
")] + [InlineData(TypeColorText.Danger, @"
")] + [InlineData(TypeColorText.Light, @"
")] + [InlineData(TypeColorText.Dark, @"
")] + [InlineData(TypeColorText.Muted, @"
")] + public void NameColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + NameColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the attribute control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("value", @"
value
")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the attribute control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("http://example.com", @"
")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + Uri = uri != null ? new Uri(uri) : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAvatar.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAvatar.cs new file mode 100644 index 00000000..bbe8e8a4 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlAvatar.cs @@ -0,0 +1,141 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the avatar control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlAvatar + { + /// + /// Tests the id property of the avatar control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("id", @"
")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAvatar(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the user property of the avatar control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("me", @"
mme
")] + public void User(string user, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAvatar() + { + User = user + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the image property of the avatar control. + /// + [Theory] + [InlineData(null, @"
")] + [InlineData("http://example.com", @"
")] + public void Image(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAvatar() + { + Image = uri != null ? new Uri(uri) : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the avatar control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
")] + [InlineData(TypeColorText.Primary, @"
")] + [InlineData(TypeColorText.Secondary, @"
")] + [InlineData(TypeColorText.Info, @"
")] + [InlineData(TypeColorText.Success, @"
")] + [InlineData(TypeColorText.Warning, @"
")] + [InlineData(TypeColorText.Danger, @"
")] + [InlineData(TypeColorText.Light, @"
")] + [InlineData(TypeColorText.Dark, @"
")] + [InlineData(TypeColorText.Muted, @"
")] + public void TextColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAvatar() + { + TextColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the avatar control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
")] + [InlineData(TypeColorBackground.Primary, @"
")] + [InlineData(TypeColorBackground.Secondary, @"
")] + [InlineData(TypeColorBackground.Warning, @"
")] + [InlineData(TypeColorBackground.Danger, @"
")] + [InlineData(TypeColorBackground.Dark, @"
")] + [InlineData(TypeColorBackground.Light, @"
")] + [InlineData(TypeColorBackground.Transparent, @"
")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAvatar() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlBadge.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlBadge.cs new file mode 100644 index 00000000..bee70919 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlBadge.cs @@ -0,0 +1,166 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the badge control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlBadge + { + /// + /// Tests the id property of the badge control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBadge(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the badge control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(-10, @"-10")] + [InlineData(0, @"0")] + [InlineData(10, @"10")] + public void Value(int? value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBadge() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the badge control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("http://example.com", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBadge() + { + Uri = uri != null ? new Uri(uri) : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the badge control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"")] + [InlineData(TypeColorText.Primary, @"")] + [InlineData(TypeColorText.Secondary, @"")] + [InlineData(TypeColorText.Info, @"")] + [InlineData(TypeColorText.Success, @"")] + [InlineData(TypeColorText.Warning, @"")] + [InlineData(TypeColorText.Danger, @"")] + [InlineData(TypeColorText.Light, @"")] + [InlineData(TypeColorText.Dark, @"")] + [InlineData(TypeColorText.Muted, @"")] + public void TextColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBadge() + { + TextColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the badge control. + /// + [Theory] + [InlineData(TypeColorBackgroundBadge.Default, @"")] + [InlineData(TypeColorBackgroundBadge.Primary, @"")] + [InlineData(TypeColorBackgroundBadge.Secondary, @"")] + [InlineData(TypeColorBackgroundBadge.Warning, @"")] + [InlineData(TypeColorBackgroundBadge.Danger, @"")] + [InlineData(TypeColorBackgroundBadge.Dark, @"")] + [InlineData(TypeColorBackgroundBadge.Light, @"")] + [InlineData(TypeColorBackgroundBadge.Transparent, @"")] + public void BackgroundColor(TypeColorBackgroundBadge backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBadge() + { + BackgroundColor = new PropertyColorBackgroundBadge(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the pill property of the badge control. + /// + [Theory] + [InlineData(TypePillBadge.None, @"")] + [InlineData(TypePillBadge.Pill, @"")] + public void Pill(TypePillBadge pill, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBadge() + { + Pill = pill + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlBreadcrumb.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlBreadcrumb.cs new file mode 100644 index 00000000..9079f845 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlBreadcrumb.cs @@ -0,0 +1,170 @@ +ο»Ώusing WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the breadcrumb control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlBreadcrumb + { + /// + /// Tests the id property of the breadcrumb control. + /// + [Theory] + [InlineData(null, @"
    ")] + [InlineData("id", @"
      ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBreadcrumb(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the breadcrumb control. + /// + [Theory] + [InlineData(null, @"
        ")] + [InlineData("http://example.com", @"
          ")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBreadcrumb { Uri = new UriEndpoint(uri) }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the empty name property of the breadcrumb control. + /// + [Theory] + [InlineData(null, @"
            ")] + [InlineData("Home", @"
              ")] + public void EmptyName(string emptyName, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBreadcrumb { EmptyName = emptyName, Uri = new UriEndpoint("http://example.com") }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the breadcrumb control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"
                ")] + [InlineData(TypeSizeButton.Small, @"
                  ")] + [InlineData(TypeSizeButton.Large, @"
                    ")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBreadcrumb { Size = size, Uri = new UriEndpoint("http://example.com") }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the prefix property of the breadcrumb control. + /// + [Theory] + [InlineData(null, @"
                      ")] + [InlineData("Prefix", @"
                      1. Prefix
                      ")] + public void Prefix(string prefix, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBreadcrumb { Prefix = prefix, Uri = new UriEndpoint("http://example.com") }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the take last property of the breadcrumb control. + /// + [Theory] + [InlineData((ushort)5, @"
                        ")] + [InlineData(3, @"
                          ")] + public void TakeLast(ushort takeLast, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlBreadcrumb { TakeLast = takeLast, Uri = new UriEndpoint("http://example.com") }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the render function of the breadcrumb control. + /// + [Theory] + [InlineData(null, @"
                            *
                          ")] + [InlineData("http://localhost:80/app/page", @"
                          1. abc
                          ")] + public void Render(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(typeof(TestApplication)).FirstOrDefault(); + var context = UnitTestControlFixture.CrerateRenderContextMock(application); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var uriResource = new UriEndpoint(uri); + var control = new ControlBreadcrumb() + { + Uri = uriResource + }; + + if (uriResource.PathSegments.LastOrDefault() != null) + { + uriResource.PathSegments.LastOrDefault().Display ??= "abc"; + } + + var uriProperty = context.Request.GetType().GetProperty("Uri"); + uriProperty.SetValue(context.Request, uriResource); + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlButton.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlButton.cs new file mode 100644 index 00000000..9507c5b6 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlButton.cs @@ -0,0 +1,223 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlButton + { + /// + /// Tests the id property of the button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the button control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"")] + [InlineData(TypeSizeButton.Small, @"")] + [InlineData(TypeSizeButton.Large, @"")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the backgroundcolor property of the button control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"")] + [InlineData(TypeColorButton.Primary, @"")] + [InlineData(TypeColorButton.Secondary, @"")] + [InlineData(TypeColorButton.Warning, @"")] + [InlineData(TypeColorButton.Danger, @"")] + [InlineData(TypeColorButton.Dark, @"")] + public void BackgroundColor(TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton() + { + BackgroundColor = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the button control. + /// + [Theory] + [InlineData(false, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Primary, @"")] + [InlineData(true, TypeColorButton.Secondary, @"")] + [InlineData(true, TypeColorButton.Warning, @"")] + [InlineData(true, TypeColorButton.Danger, @"")] + [InlineData(true, TypeColorButton.Dark, @"")] + public void Outline(bool outline, TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton() + { + Outline = outline, + BackgroundColor = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the button control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"")] + [InlineData(TypeBlockButton.Block, @"")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButton() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlButton(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlButton(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlButton(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlButton(null); + var control5 = new ControlButton(null); + var control6 = new ControlButton(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }])); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + Assert.Equal(@"", html4.Trim()); + Assert.Equal(@"", html5.Trim()); + Assert.Equal(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlButtonLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlButtonLink.cs new file mode 100644 index 00000000..73c91486 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlButtonLink.cs @@ -0,0 +1,235 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the buttonlink control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlButtonLink + { + /// + /// Tests the id property of the buttonlink control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the text property of the buttonlink control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the tooltip property of the buttonlink control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                          b", @"b"" data-bs-toggle=""tooltip"">")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the size property of the buttonlink control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"")] + [InlineData(TypeSizeButton.Small, @"")] + [InlineData(TypeSizeButton.Large, @"")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the backgroundcolor property of the buttonlink control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"")] + [InlineData(TypeColorButton.Primary, @"")] + [InlineData(TypeColorButton.Secondary, @"")] + [InlineData(TypeColorButton.Warning, @"")] + [InlineData(TypeColorButton.Danger, @"")] + [InlineData(TypeColorButton.Dark, @"")] + public void BackgroundColor(TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + BackgroundColor = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the outline property of the buttonlink control. + /// + [Theory] + [InlineData(false, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Primary, @"")] + [InlineData(true, TypeColorButton.Secondary, @"")] + [InlineData(true, TypeColorButton.Warning, @"")] + [InlineData(true, TypeColorButton.Danger, @"")] + [InlineData(true, TypeColorButton.Dark, @"")] + public void Outline(bool outline, TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + Outline = outline, + BackgroundColor = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the block property of the buttonlink control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"")] + [InlineData(TypeBlockButton.Block, @"")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the icon property of the buttonlink control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlButtonLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the content property of the buttonlink control. + /// + [Fact] + public void Content() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlButtonLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlButtonLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlButtonLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + // test execution + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCanvas.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCanvas.cs new file mode 100644 index 00000000..4ad9bb3f --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCanvas.cs @@ -0,0 +1,116 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the canvas control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlCanvas + { + /// + /// Tests the id property of the canvas control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCanvas(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the canvas control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCanvas() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the width property of the canvas control. + /// + [Theory] + [InlineData(TypeWidth.Default, @"")] + [InlineData(TypeWidth.TwentyFive, @"")] + [InlineData(TypeWidth.Fifty, @"")] + [InlineData(TypeWidth.SeventyFive, @"")] + [InlineData(TypeWidth.OneHundred, @"")] + public void Width(TypeWidth width, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCanvas() + { + Width = width, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the height property of the canvas control. + /// + [Theory] + [InlineData(TypeHeight.Default, @"")] + [InlineData(TypeHeight.TwentyFive, @"")] + [InlineData(TypeHeight.Fifty, @"")] + [InlineData(TypeHeight.SeventyFive, @"")] + [InlineData(TypeHeight.OneHundred, @"")] + public void Height(TypeHeight height, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCanvas() + { + Height = height, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCardCounter.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCardCounter.cs new file mode 100644 index 00000000..b3fd11ef --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCardCounter.cs @@ -0,0 +1,193 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the card counter control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlCardCounter + { + /// + /// Tests the id property of the card counter control. + /// + [Theory] + [InlineData(null, @"

                          ")] + [InlineData("id", @"

                          ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the card counter control. + /// + [Theory] + [InlineData(null, @"

                          ")] + [InlineData("abc", @"

                          abc
                          ")] + [InlineData("webexpress.webui:plugin.name", @"

                          WebExpress.WebUI
                          ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the card counter control. + /// + [Theory] + [InlineData(null, @"

                          ")] + [InlineData(-10, @"

                          -10

                          ")] + [InlineData(0, @"

                          0

                          ")] + [InlineData(10, @"

                          10

                          ")] + public void Value(int? value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter() + { + Value = value, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the card counter control. + /// + [Theory] + [InlineData(null, @"

                          ")] + [InlineData(0u, @"

                          ")] + [InlineData(10u, @"

                          ")] + public void Progress(uint? value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter() + { + Progress = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the card counter control. + /// + [Theory] + [InlineData(null, @"

                          ")] + [InlineData(typeof(IconStar), @"

                          ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the card counter control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"*")] + [InlineData(TypeColorText.Primary, @"*")] + [InlineData(TypeColorText.Secondary, @"*")] + [InlineData(TypeColorText.Info, @"*")] + [InlineData(TypeColorText.Success, @"*")] + [InlineData(TypeColorText.Warning, @"*")] + [InlineData(TypeColorText.Danger, @"*")] + [InlineData(TypeColorText.Light, @"*")] + [InlineData(TypeColorText.Dark, @"*")] + [InlineData(TypeColorText.Muted, @"*")] + public void TextColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter() + { + TextColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the card counter control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"*")] + [InlineData(TypeColorBackground.Primary, @"*")] + [InlineData(TypeColorBackground.Secondary, @"*")] + [InlineData(TypeColorBackground.Warning, @"*")] + [InlineData(TypeColorBackground.Danger, @"*")] + [InlineData(TypeColorBackground.Dark, @"*")] + [InlineData(TypeColorBackground.Light, @"<*/span>")] + [InlineData(TypeColorBackground.Transparent, @"*")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCardCounter() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCarousel.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCarousel.cs new file mode 100644 index 00000000..e8043bfd --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlCarousel.cs @@ -0,0 +1,120 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the carousel control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlCarousel + { + /// + /// Tests the id property of the carousel control. + /// + [Theory] + [InlineData(null, @"
                          *
                          ")] + [InlineData("id", @"
                            *
                          ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCarousel(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the carousel control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                          *
                          ")] + [InlineData(TypeColorText.Primary, @"
                            *
                          ")] + [InlineData(TypeColorText.Secondary, @"
                            *
                          ")] + [InlineData(TypeColorText.Info, @"
                            *
                          ")] + [InlineData(TypeColorText.Success, @"
                            *
                          ")] + [InlineData(TypeColorText.Warning, @"
                            *
                          ")] + [InlineData(TypeColorText.Danger, @"
                            *
                          ")] + [InlineData(TypeColorText.Light, @"
                            *
                          ")] + [InlineData(TypeColorText.Dark, @"
                            *
                          ")] + [InlineData(TypeColorText.Muted, @"
                            *
                          ")] + public void TextColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCarousel() + { + TextColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the carousel control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Primary, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Secondary, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Warning, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Danger, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Dark, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Light, @"
                          *
                          ")] + [InlineData(TypeColorBackground.Transparent, @"
                          *
                          ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlCarousel() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the carousel control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                          *
                          *
                          ")] + [InlineData(typeof(ControlLink), @"
                          **
                          ")] + [InlineData(typeof(ControlImage), @"
                          **
                          ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var item = new ControlCarouselItem(childInstance); + var control = new ControlCarousel(); + + // test execution + control.Add(item); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlChart.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlChart.cs new file mode 100644 index 00000000..3c01440b --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlChart.cs @@ -0,0 +1,279 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the chart control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlChart + { + /// + /// Tests the id property of the chart control. + /// + [Theory] + [InlineData(null, @"
                          ")] + [InlineData("id", @"
                          ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the chart control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                          ")] + [InlineData(TypeColorText.Primary, @"
                          ")] + [InlineData(TypeColorText.Secondary, @"
                          ")] + [InlineData(TypeColorText.Info, @"
                          ")] + [InlineData(TypeColorText.Success, @"
                          ")] + [InlineData(TypeColorText.Warning, @"
                          ")] + [InlineData(TypeColorText.Danger, @"
                          ")] + [InlineData(TypeColorText.Light, @"
                          ")] + [InlineData(TypeColorText.Dark, @"
                          ")] + [InlineData(TypeColorText.Muted, @"
                          ")] + public void TextColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + TextColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the chart control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                          ")] + [InlineData(TypeColorBackground.Primary, @"
                          ")] + [InlineData(TypeColorBackground.Secondary, @"
                          ")] + [InlineData(TypeColorBackground.Warning, @"
                          ")] + [InlineData(TypeColorBackground.Danger, @"
                          ")] + [InlineData(TypeColorBackground.Dark, @"
                          ")] + [InlineData(TypeColorBackground.Light, @"
                          ")] + [InlineData(TypeColorBackground.Transparent, @"
                          ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the type property of the chart control. + /// + [Theory] + [InlineData(TypeChart.Bar, @"
                          ")] + [InlineData(TypeChart.Line, @"
                          ")] + public void Type(TypeChart type, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + Type = type + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title property of the chart control. + /// + [Theory] + [InlineData(null, @"
                          ")] + [InlineData("Chart Title", @"
                          ")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + Title = title + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title of the x-axis property of the chart control. + /// + [Theory] + [InlineData(null, @"
                          ")] + [InlineData("X-Axis Title", @"
                          ")] + public void TitleX(string titleX, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + TitleX = titleX + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title of the y-axis property of the chart control. + /// + [Theory] + [InlineData(null, @"
                          ")] + [InlineData("Y-Axis Title", @"
                          ")] + public void TitleY(string titleY, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + TitleY = titleY + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the width property of the chart control. + /// + [Theory] + [InlineData(0, @"
                          ")] + [InlineData(500, @"
                          ")] + public void Width(int width, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + Width = width + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the height property of the chart control. + /// + [Theory] + [InlineData(0, @"
                          ")] + [InlineData(300, @"
                          ")] + public void Height(int height, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + Height = height + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the minimum property of the chart control. + /// + [Theory] + [InlineData(float.MinValue, @"
                          ")] + [InlineData(0f, @"
                          ")] + public void Minimum(float minimum, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + Minimum = minimum + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the maximum property of the chart control. + /// + [Theory] + [InlineData(float.MaxValue, @"
                          ")] + [InlineData(100f, @"
                          ")] + public void Maximum(float maximum, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlChart() + { + Maximum = maximum + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdown.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdown.cs new file mode 100644 index 00000000..cbd5683d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdown.cs @@ -0,0 +1,322 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the dropdown control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlDropdown + { + /// + /// Tests the id property of the dropdown control. + /// + [Theory] + [InlineData(null, @"
                            ")] + [InlineData("id", @"
                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the dropdown control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"
                                ")] + [InlineData(TypeColorButton.Primary, @"
                                  ")] + [InlineData(TypeColorButton.Secondary, @"
                                    ")] + [InlineData(TypeColorButton.Info, @"
                                      ")] + [InlineData(TypeColorButton.Warning, @"
                                        ")] + [InlineData(TypeColorButton.Danger, @"
                                          ")] + [InlineData(TypeColorButton.Light, @"
                                            ")] + [InlineData(TypeColorButton.Dark, @"
                                              ")] + public void BackgroundColor(TypeColorButton backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + BackgroundColor = new PropertyColorButton(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the dropdown control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"
                                                ")] + [InlineData(TypeSizeButton.Small, @"
                                                  ")] + [InlineData(TypeSizeButton.Large, @"
                                                    ")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the dropdown control. + /// + [Theory] + [InlineData(false, @"
                                                      ")] + [InlineData(true, @"
                                                        ")] + public void Outline(bool outline, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Outline = outline, + BackgroundColor = new PropertyColorButton(TypeColorButton.Primary) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the dropdown control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"
                                                          ")] + [InlineData(TypeBlockButton.Block, @"
                                                            ")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the toogle property of the dropdown control. + /// + [Theory] + [InlineData(TypeToggleDropdown.None, @"
                                                              ")] + [InlineData(TypeToggleDropdown.Toggle, @"
                                                                ")] + public void Toogle(TypeToggleDropdown toogle, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Toogle = toogle + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                  ")] + [InlineData("abc", @"
                                                                    ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                      ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                        ")] + [InlineData("abc", @"
                                                                          ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                            ")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Tooltip = tooltip, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                              ")] + [InlineData("abc", @"
                                                                                ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                  ")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Value = value, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the icon property of the dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                                    ")] + [InlineData(typeof(IconStar), @"
                                                                                      ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the active property of the dropdown control. + /// + [Theory] + [InlineData(TypeActive.None, @"
                                                                                        ")] + [InlineData(TypeActive.Active, @"
                                                                                          ")] + [InlineData(TypeActive.Disabled, @"
                                                                                            ")] + public void Active(TypeActive active, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + Active = active + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the alignment menu property of the dropdown control. + /// + [Theory] + [InlineData(TypeAlignmentDropdownMenu.Default, @"
                                                                                              ")] + [InlineData(TypeAlignmentDropdownMenu.Right, @"
                                                                                                ")] + public void AlignmentMenu(TypeAlignmentDropdownMenu alignmentMenu, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown() + { + AlignmentMenu = alignmentMenu + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the dropdown control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdown(); + + // test execution + control.Add(new ControlDropdownItemLink() { Text = "abc" }); + + var html = control.Render(context, visualTree); + + Assert.Equal(@"", html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemDivider.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemDivider.cs new file mode 100644 index 00000000..9d206d54 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemDivider.cs @@ -0,0 +1,35 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the dropdown item divider control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlDropdownItemDivider + { + /// + /// Tests the id property of the dropdown item divider control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemDivider(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemHeader.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemHeader.cs new file mode 100644 index 00000000..f6113cad --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemHeader.cs @@ -0,0 +1,59 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the dropdown item header control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlDropdownItemHeader + { + /// + /// Tests the id property of the dropdown item header control. + /// + [Theory] + [InlineData(null, @"
                                                                                              • ")] + [InlineData("id", @"
                                                                                              • ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemHeader(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the dropdown item header control. + /// + [Theory] + [InlineData(null, @"
                                                                                              • ")] + [InlineData("abc", @"
                                                                                              • abc
                                                                                              • ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                              • WebExpress.WebUI
                                                                                              • ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemHeader() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemLink.cs new file mode 100644 index 00000000..5b92f78f --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlDropdownItemLink.cs @@ -0,0 +1,222 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the dropdown item link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlDropdownItemLink + { + /// + /// Tests the id property of the dropdown item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the text property of the dropdown item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the uri property of the dropdown item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the title property of the dropdown item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the target property of the dropdown item link control. + /// + [Theory] + [InlineData(TypeTarget.None, @"")] + [InlineData(TypeTarget.Blank, @"")] + [InlineData(TypeTarget.Self, @"")] + [InlineData(TypeTarget.Parent, @"")] + [InlineData(TypeTarget.Framename, @"")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the tooltip property of the dropdown item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                                                                                                b", @"b"" data-bs-toggle=""tooltip"">")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the icon property of the dropdown item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlDropdownItemLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the add function of the dropdown item link control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlDropdownItemLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlDropdownItemLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlDropdownItemLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlDropdownItemLink(null); + var control5 = new ControlDropdownItemLink(null); + var control6 = new ControlDropdownItemLink(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + Assert.Equal(@"", html4.Trim()); + Assert.Equal(@"", html5.Trim()); + Assert.Equal(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlForm.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlForm.cs new file mode 100644 index 00000000..90ecf676 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlForm.cs @@ -0,0 +1,229 @@ +ο»Ώusing WebExpress.WebCore.WebMessage; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlForm + { + /// + /// Tests the id property of the form control. + /// + [Theory] + [InlineData(null, @"
                                                                                                *
                                                                                                ")] + [InlineData("id", @"
                                                                                                *
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the backgroundcolor property of the form control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                *
                                                                                                ")] + public void BackgroundColor(TypeColorBackground color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + BackgroundColor = new PropertyColorBackground(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form control. + /// + [Theory] + [InlineData(null, @"
                                                                                                *
                                                                                                ")] + [InlineData("abc", @"
                                                                                                *
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + // + /// Tests the method property of the form control. + /// + [Theory] + [InlineData(RequestMethod.NONE, @"
                                                                                                *
                                                                                                ")] + [InlineData(RequestMethod.POST, @"
                                                                                                *
                                                                                                ")] + [InlineData(RequestMethod.GET, @"
                                                                                                *
                                                                                                ")] + public void Method(RequestMethod method, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + Method = method + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the form layout property of the form control. + /// + [Theory] + [InlineData(TypeLayoutForm.Default, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeLayoutForm.Inline, @"
                                                                                                *
                                                                                                ")] + public void FormLayout(TypeLayoutForm formLayout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + FormLayout = formLayout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the item layout property of the form control. + /// + [Theory] + [InlineData(TypeLayoutFormItem.Horizontal, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeLayoutFormItem.Vertical, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeLayoutFormItem.Mix, @"
                                                                                                *
                                                                                                ")] + public void ItemLayout(TypeLayoutFormItem itemLayout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + ItemLayout = itemLayout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the justify property of the form control. + /// + [Theory] + [InlineData(TypeJustifiedFlexbox.None, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeJustifiedFlexbox.Start, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeJustifiedFlexbox.Around, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeJustifiedFlexbox.Between, @"
                                                                                                *
                                                                                                ")] + [InlineData(TypeJustifiedFlexbox.End, @"
                                                                                                *
                                                                                                ")] + public void Justify(TypeJustifiedFlexbox justify, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + Justify = justify, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests a empty form. + /// + [Fact] + public void EmptyForm() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm(); + + var html = control.Render(context, visualTree); + + // test execution + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                + /// Tests a empty form. + /// + [Fact] + public void EmptyFormChangeSubmitText() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm(); + control.AddPrimaryButton(new ControlFormItemButtonSubmit("") { Text = "sendbutton" }); + + var html = control.Render(context, visualTree); + + // test execution + Assert.Contains(@"sendbutton", html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormInline.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormInline.cs new file mode 100644 index 00000000..7239f5a5 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormInline.cs @@ -0,0 +1,88 @@ +ο»Ώusing WebExpress.WebCore.WebMessage; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the inline form control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormInline + { + /// + /// Tests the id property of the pagination control. + /// + [Theory] + [InlineData(null, @"*
                                                                                                ")] + [InlineData("id", @"
                                                                                                *
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm(id) + { + FormLayout = TypeLayoutForm.Inline, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form control. + /// + [Theory] + [InlineData(null, @"
                                                                                                *
                                                                                                ")] + [InlineData("abc", @"
                                                                                                *
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + FormLayout = TypeLayoutForm.Inline, + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the method property of the form control. + /// + [Theory] + [InlineData(RequestMethod.NONE, @"
                                                                                                *
                                                                                                ")] + [InlineData(RequestMethod.POST, @"
                                                                                                *
                                                                                                ")] + [InlineData(RequestMethod.GET, @"
                                                                                                *
                                                                                                ")] + public void Method(RequestMethod method, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlForm() + { + FormLayout = TypeLayoutForm.Inline, + Method = method + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButton.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButton.cs new file mode 100644 index 00000000..7aab7347 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButton.cs @@ -0,0 +1,264 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemButton + { + /// + /// Tests the id property of the form button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the form button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the form button control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"")] + [InlineData(TypeSizeButton.Small, @"")] + [InlineData(TypeSizeButton.Large, @"")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the backgroundcolor property of the form button control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.White, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + BackgroundColor = new PropertyColorBackground(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the form button control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"")] + [InlineData(TypeColorButton.Primary, @"")] + [InlineData(TypeColorButton.Secondary, @"")] + [InlineData(TypeColorButton.Warning, @"")] + [InlineData(TypeColorButton.Danger, @"")] + [InlineData(TypeColorButton.Dark, @"")] + [InlineData(TypeColorButton.Light, @"")] + public void Color(TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + Color = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the form button control. + /// + [Theory] + [InlineData(false, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Primary, @"")] + [InlineData(true, TypeColorButton.Secondary, @"")] + [InlineData(true, TypeColorButton.Warning, @"")] + [InlineData(true, TypeColorButton.Danger, @"")] + [InlineData(true, TypeColorButton.Dark, @"")] + [InlineData(true, TypeColorButton.Light, @"")] + public void Outline(bool outline, TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + Outline = outline, + Color = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the form button control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"")] + [InlineData(TypeBlockButton.Block, @"")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the form button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButton() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the form button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlFormItemButton(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlFormItemButton(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlFormItemButton(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlFormItemButton(null); + var control5 = new ControlFormItemButton(null); + var control6 = new ControlFormItemButton(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }])); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html1.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html2.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html3.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html4.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html5.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButtonReset.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButtonReset.cs new file mode 100644 index 00000000..24fa3987 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButtonReset.cs @@ -0,0 +1,264 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form reset button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemButtonReset + { + /// + /// Tests the id property of the form submit button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the form reset button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the form reset button control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"")] + [InlineData(TypeSizeButton.Small, @"")] + [InlineData(TypeSizeButton.Large, @"")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the backgroundcolor property of the form reset button control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.White, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + BackgroundColor = new PropertyColorBackground(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the color property of the form reset button control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"")] + [InlineData(TypeColorButton.Primary, @"")] + [InlineData(TypeColorButton.Secondary, @"")] + [InlineData(TypeColorButton.Warning, @"")] + [InlineData(TypeColorButton.Danger, @"")] + [InlineData(TypeColorButton.Dark, @"")] + [InlineData(TypeColorButton.Light, @"")] + public void Color(TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + Color = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the form reset button control. + /// + [Theory] + [InlineData(false, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Primary, @"")] + [InlineData(true, TypeColorButton.Secondary, @"")] + [InlineData(true, TypeColorButton.Warning, @"")] + [InlineData(true, TypeColorButton.Danger, @"")] + [InlineData(true, TypeColorButton.Dark, @"")] + [InlineData(true, TypeColorButton.Light, @"")] + public void Outline(bool outline, TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + Outline = outline, + Color = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the form reset button control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"")] + [InlineData(TypeBlockButton.Block, @"")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the form reset button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonReset() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the form reset button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlFormItemButtonReset(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlFormItemButtonReset(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlFormItemButtonReset(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlFormItemButtonReset(null); + var control5 = new ControlFormItemButtonReset(null); + var control6 = new ControlFormItemButtonReset(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }])); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html1.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html2.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html3.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html4.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html5.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButtonSubmit.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButtonSubmit.cs new file mode 100644 index 00000000..272744e9 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemButtonSubmit.cs @@ -0,0 +1,264 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form submit button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemButtonSubmit + { + /// + /// Tests the id property of the form submit button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the form submit button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the form submit button control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"")] + [InlineData(TypeSizeButton.Small, @"")] + [InlineData(TypeSizeButton.Large, @"")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the backgroundcolor property of the form submit button control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.White, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + BackgroundColor = new PropertyColorBackground(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the color property of the form submit button control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"")] + [InlineData(TypeColorButton.Primary, @"")] + [InlineData(TypeColorButton.Secondary, @"")] + [InlineData(TypeColorButton.Warning, @"")] + [InlineData(TypeColorButton.Danger, @"")] + [InlineData(TypeColorButton.Dark, @"")] + [InlineData(TypeColorButton.Light, @"")] + public void Color(TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + Color = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the form submit button control. + /// + [Theory] + [InlineData(false, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Default, @"")] + [InlineData(true, TypeColorButton.Primary, @"")] + [InlineData(true, TypeColorButton.Secondary, @"")] + [InlineData(true, TypeColorButton.Warning, @"")] + [InlineData(true, TypeColorButton.Danger, @"")] + [InlineData(true, TypeColorButton.Dark, @"")] + [InlineData(true, TypeColorButton.Light, @"")] + public void Outline(bool outline, TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + Outline = outline, + Color = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the form submit button control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"")] + [InlineData(TypeBlockButton.Block, @"")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the form submit button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemButtonSubmit() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the form submit button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlFormItemButtonSubmit(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlFormItemButtonSubmit(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlFormItemButtonSubmit(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlFormItemButtonSubmit(null); + var control5 = new ControlFormItemButtonSubmit(null); + var control6 = new ControlFormItemButtonSubmit(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }])); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html1.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html2.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html3.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html4.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html5.Trim()); + AssertExtensions.EqualWithPlaceholders(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnHorizontal.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnHorizontal.cs new file mode 100644 index 00000000..241bb703 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnHorizontal.cs @@ -0,0 +1,60 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item group column horizontal control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemGroupColumnHorizontal + { + /// + /// Tests the id property of the form item group column horizontal control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupColumnHorizontal(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form item group column horizontal control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupColumnHorizontal() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnMix.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnMix.cs new file mode 100644 index 00000000..d9d22c39 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnMix.cs @@ -0,0 +1,60 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item group column mix control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemGroupColumnMix + { + /// + /// Tests the id property of the form item group column mix control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupColumnMix(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form item group column mix control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupColumnMix() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnVertical.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnVertical.cs new file mode 100644 index 00000000..1edbedde --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupColumnVertical.cs @@ -0,0 +1,60 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item group column vertical control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemGroupColumnVertical + { + /// + /// Tests the id property of the form item group column vertical control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupColumnVertical(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form item group column vertical control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupColumnVertical() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupHorizontal.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupHorizontal.cs new file mode 100644 index 00000000..4bc28e95 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupHorizontal.cs @@ -0,0 +1,60 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item group horizontal control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemGroupHorizontal + { + /// + /// Tests the id property of the form item group horizontal control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupHorizontal(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form item group horizontal control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupHorizontal() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupMix.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupMix.cs new file mode 100644 index 00000000..f3afbef7 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupMix.cs @@ -0,0 +1,60 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item group mix control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemGroupMix + { + /// + /// Tests the id property of the form item group mix control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupMix(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form item group mix control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupMix() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupVertical.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupVertical.cs new file mode 100644 index 00000000..89e3dfac --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemGroupVertical.cs @@ -0,0 +1,60 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item group vertical control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemGroupVertical + { + /// + /// Tests the id property of the form item group vertical control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupVertical(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form item group vertical control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemGroupVertical() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemHelpText.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemHelpText.cs new file mode 100644 index 00000000..450e7dbc --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemHelpText.cs @@ -0,0 +1,88 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item help text control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemHelpText + { + /// + /// Tests the id property of the form item help text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemHelpText(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the form item help text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemHelpText() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the form item help text control. + /// + [Theory] + [InlineData(TypeSizeText.Default, @"")] + [InlineData(TypeSizeText.ExtraSmall, @"")] + [InlineData(TypeSizeText.Small, @"")] + [InlineData(TypeSizeText.Large, @"")] + [InlineData(TypeSizeText.ExtraLarge, @"")] + public void Size(TypeSizeText size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemHelpText() + { + Size = new PropertySizeText(size) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputCheckbox.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputCheckbox.cs new file mode 100644 index 00000000..86b3280d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputCheckbox.cs @@ -0,0 +1,108 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item input checkbox control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputCheckbox + { + /// + /// Tests the id property of the form item input checkbox control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCheckbox(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Inline property of the form item input checkbox control. + /// + [Theory] + [InlineData(false, @"
                                                                                                ")] + [InlineData(true, @"
                                                                                                ")] + public void Inline(bool inline, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCheckbox + { + Inline = inline + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Description property of the form item input checkbox control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("description", @"
                                                                                                ")] + public void Description(string description, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCheckbox + { + Description = description + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Pattern property of the form item input checkbox control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("pattern", @"
                                                                                                ")] + public void Pattern(string pattern, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCheckbox + { + Pattern = pattern + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputCombobox.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputCombobox.cs new file mode 100644 index 00000000..3809ebce --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputCombobox.cs @@ -0,0 +1,200 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item input combobox control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputCombobox + { + /// + /// Tests the id property of the form item input combobox control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Icon property of the form item input combobox control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Label property of the form item input combobox control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("label", @"")] + public void Label(string label, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox + { + Label = label + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the help property of the form item input combobox control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("help", @"")] + public void Help(string help, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox + { + Help = help + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the disabled property of the form item input combobox control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void Disabled(bool disabled, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox + { + Disabled = disabled + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the prepend property of the form item input combobox control. + /// + [Fact] + public void Prepend() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox(); + control.Prepend.Add(new ControlText { Text = "prepend" }); + + // test execution + var html = control.Render(context, visualTree); + + var expected = @""; + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the append property of the form item input combobox control. + /// + [Fact] + public void Append() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox(); + control.Append.Add(new ControlText { Text = "append" }); + + // test execution + var html = control.Render(context, visualTree); + + var expected = @""; + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tag property of the form item input combobox control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("tag", @"")] + public void Tag(object tag, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputCombobox + { + Tag = tag + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputDatepicker.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputDatepicker.cs new file mode 100644 index 00000000..92e3ae10 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputDatepicker.cs @@ -0,0 +1,108 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form datepicker control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputDatepicker + { + /// + /// Tests the id property of the form datepicker control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputDatepicker(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form datepicker control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputDatepicker() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form datepicker control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("2023-10-10", @"")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputDatepicker() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the required property of the form datepicker control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void Required(bool required, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputDatepicker() + { + Required = required + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputFile.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputFile.cs new file mode 100644 index 00000000..9b0c3f1c --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputFile.cs @@ -0,0 +1,106 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form file input control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputFile + { + /// + /// Tests the id property of the form file input control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputFile(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form file input control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputFile() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the required property of the form file input control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void Required(bool required, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputFile() + { + Required = required + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the accept property of the form file input control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("image/*", @"")] + public void Accept(string accept, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputFile(); + + // test execution + control.AddAcceptFile(accept); + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputGroup.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputGroup.cs new file mode 100644 index 00000000..10321c3c --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputGroup.cs @@ -0,0 +1,84 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form group control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputGroup + { + /// + /// Tests the id property of the form label control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputGroup(id, new ControlFormItemGroupColumnVertical()) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form group control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputGroup(null, new ControlFormItemGroupColumnVertical()) + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form group control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputGroup(null, new ControlFormItemGroupColumnVertical()) + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputHidden.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputHidden.cs new file mode 100644 index 00000000..3371ab71 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputHidden.cs @@ -0,0 +1,84 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form hidden control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputHidden + { + /// + /// Tests the id property of the form hidden control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputHidden(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form hidden control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputHidden() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form hidden control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputHidden() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputMove.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputMove.cs new file mode 100644 index 00000000..373307e7 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputMove.cs @@ -0,0 +1,107 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form move control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputMove + { + /// + /// Tests the id property of the form move control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputMove(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form move control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputMove() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form move control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputMove() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Add method of the form move control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputMove() + { + }; + + // test execution + control.Add(new ControlFormItemInputSelectionItem() { Label = "label" }); + var html = control.Render(context, visualTree); + + Assert.NotEmpty(control.Options); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                ", html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputRadio.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputRadio.cs new file mode 100644 index 00000000..4a8a00fe --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputRadio.cs @@ -0,0 +1,108 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item input radio control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputRadio + { + /// + /// Tests the id property of the form item input radio control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputRadio(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Inline property of the form item input radio control. + /// + [Theory] + [InlineData(false, @"
                                                                                                ")] + [InlineData(true, @"
                                                                                                ")] + public void Inline(bool inline, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputRadio + { + Inline = inline + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Description property of the form item input radio control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("description", @"
                                                                                                ")] + public void Description(string description, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputRadio + { + Description = description + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Pattern property of the form item input radio control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("pattern", @"
                                                                                                ")] + public void Pattern(string pattern, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputRadio + { + Pattern = pattern + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputSelection.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputSelection.cs new file mode 100644 index 00000000..886c1b9d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputSelection.cs @@ -0,0 +1,179 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form selection control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputSelection + { + /// + /// Tests the id property of the form selection control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form selection control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form selection control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                ")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the placeholder property of the form selection control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("Select an option", @"
                                                                                                ")] + public void Placeholder(string placeholder, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection() + { + Placeholder = placeholder + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the HideDescription property of the form selection control. + /// + [Theory] + [InlineData(false, @"
                                                                                                ")] + [InlineData(true, @"
                                                                                                ")] + public void HideDescription(bool hideDescription, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection() + { + HideDescription = hideDescription + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the MultiSelect property of the form selection control. + /// + [Theory] + [InlineData(false, @"
                                                                                                ")] + [InlineData(true, @"
                                                                                                ")] + public void MultiSelect(bool multiSelect, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection() + { + MultiSelect = multiSelect + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the MultiSelect property of the form selection control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputSelection() + { + }; + + // test execution + control.Add(new ControlFormItemInputSelectionItem() { Label = "label" }); + var html = control.Render(context, visualTree); + + Assert.NotEmpty(control.Options); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                ", html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputTextBox.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputTextBox.cs new file mode 100644 index 00000000..fc459795 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemInputTextBox.cs @@ -0,0 +1,281 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form text control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemInputTextBox + { + /// + /// Tests the id property of the form label control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the format property of the form text control. + /// + [Theory] + [InlineData(TypesEditTextFormat.Default, @"")] + [InlineData(TypesEditTextFormat.Multiline, @"")] + [InlineData(TypesEditTextFormat.Wysiwyg, @"")] + public void Format(TypesEditTextFormat format, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Format = format + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the description property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Description(string description, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Description = description + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the placeholder property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Placeholder(string placeholder, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Placeholder = placeholder + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the min length property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(0u, @"")] + [InlineData(10u, @"")] + public void MinLength(uint? minLength, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + MinLength = minLength + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the max length property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(0u, @"")] + [InlineData(10u, @"")] + public void MaxLength(uint? maxLength, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + MaxLength = maxLength + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the required property of the form text control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void Required(bool required, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Required = required + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the pattern property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc.*", @"")] + public void Pattern(string pattern, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Pattern = pattern + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the rows property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(0u, @"")] + [InlineData(10u, @"")] + public void Rows(uint? rows, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Rows = rows, + Format = TypesEditTextFormat.Multiline + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form text control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemInputTextBox() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemLabel.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemLabel.cs new file mode 100644 index 00000000..d1560fb0 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemLabel.cs @@ -0,0 +1,108 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form label control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemLabel + { + /// + /// Tests the id property of the form label control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemLabel(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form label control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemLabel() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the form label control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemLabel() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the form item property of the form label control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void FormItem(bool formItem, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemLabel() + { + FormItem = formItem ? new ControlFormItemInputTextBox() : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemPanel.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemPanel.cs new file mode 100644 index 00000000..f8894af6 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemPanel.cs @@ -0,0 +1,113 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form panel control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemPanel + { + /// + /// Tests the id property of the form panel control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemPanel(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the form panel control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemPanel() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the form panel control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemPanel() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the form panel control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                ")] + [InlineData(typeof(ControlLink), @"
                                                                                                ")] + [InlineData(typeof(ControlImage), @"
                                                                                                ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlFormItemPanel(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemPrepend.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemPrepend.cs new file mode 100644 index 00000000..35238888 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemPrepend.cs @@ -0,0 +1,113 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form item prepend control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemPrepend + { + /// + /// Tests the id property of the form item prepend control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemPrepend(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the form item prepend control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemPrepend() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the form item prepend control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemPrepend() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the form item prepend control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                ")] + [InlineData(typeof(ControlLink), @"
                                                                                                ")] + [InlineData(typeof(ControlImage), @"
                                                                                                ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlFormItemPrepend(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemStaticText.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemStaticText.cs new file mode 100644 index 00000000..d5c5233a --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlFormItemStaticText.cs @@ -0,0 +1,108 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the form static text control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlFormItemStaticText + { + /// + /// Tests the id property of the form static text control. + /// + [Theory] + [InlineData(null, @"

                                                                                                ")] + [InlineData("id", @"

                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemStaticText(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the form static text control. + /// + [Theory] + [InlineData(null, @"

                                                                                                ")] + [InlineData("abc", @"

                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemStaticText() + { + Name = name + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the label property of the form static text control. + /// + [Theory] + [InlineData(null, @"

                                                                                                ")] + [InlineData("abc", @"

                                                                                                ")] + public void Label(string label, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemStaticText() + { + Label = label + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the form static text control. + /// + [Theory] + [InlineData(null, @"

                                                                                                ")] + [InlineData("abc", @"

                                                                                                abc

                                                                                                ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlFormItemStaticText() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlHtml.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlHtml.cs new file mode 100644 index 00000000..ac18aa73 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlHtml.cs @@ -0,0 +1,58 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the HTML control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlHtml + { + /// + /// Tests the id property of the HTML control. + /// + [Theory] + [InlineData(null, null)] + [InlineData("id", null)] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlHtml(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the html property of the HTML control. + /// + [Theory] + [InlineData(null, null)] + [InlineData("
                                                                                                abc
                                                                                                ", @"
                                                                                                abc
                                                                                                ")] + public void Html(string rawHtml, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlHtml() + { + Html = rawHtml + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlIcon.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlIcon.cs new file mode 100644 index 00000000..fb35bdf6 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlIcon.cs @@ -0,0 +1,139 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the icon control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlIcon + { + /// + /// Tests the id property of the icon control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlIcon(id) + { + Icon = new IconStar() + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the title property of the icon control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlIcon() + { + Icon = new IconStar(), + Title = title + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the icon property of the icon control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlIcon() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the size property of the icon control. + /// + [Theory] + [InlineData(TypeSizeText.Default, @"")] + [InlineData(TypeSizeText.ExtraSmall, @"")] + [InlineData(TypeSizeText.Small, @"")] + [InlineData(TypeSizeText.Large, @"")] + [InlineData(TypeSizeText.ExtraLarge, @"")] + public void Size(TypeSizeText size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlIcon() + { + Icon = new IconStar(), + Size = new PropertySizeText(size) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the vertical alignment property of the icon control. + /// + [Theory] + [InlineData(TypeVerticalAlignment.Default, @"")] + [InlineData(TypeVerticalAlignment.Middle, @"")] + [InlineData(TypeVerticalAlignment.TextTop, @"")] + [InlineData(TypeVerticalAlignment.TextBottom, @"")] + [InlineData(TypeVerticalAlignment.Bottom, @"")] + public void VerticalAlignment(TypeVerticalAlignment verticalAlignment, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlIcon() + { + Icon = new IconStar(), + VerticalAlignment = verticalAlignment + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlImage.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlImage.cs new file mode 100644 index 00000000..2489855a --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlImage.cs @@ -0,0 +1,133 @@ +ο»Ώusing WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the image control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlImage + { + /// + /// Tests the id property of the image control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlImage(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the route property of the image control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Route(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlImage() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the width property of the image control. + /// + [Theory] + [InlineData(-1, @"")] + [InlineData(0, @"")] + [InlineData(1, @"")] + public void Width(int width, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlImage() + { + Width = width, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the height property of the image control. + /// + [Theory] + [InlineData(-1, @"")] + [InlineData(0, @"")] + [InlineData(1, @"")] + public void Height(int height, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlImage() + { + Height = height, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the image control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                                                                                                b", @"b"" data-toggle=""tooltip"" title=""a
                                                                                                b"">")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlImage() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLine.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLine.cs new file mode 100644 index 00000000..d470746b --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLine.cs @@ -0,0 +1,64 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the image control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlLine + { + /// + /// Tests the id property of the line control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLine(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the color property of the line control. + /// + [Theory] + [InlineData(TypeColorLine.Default, @"
                                                                                                ")] + [InlineData(TypeColorLine.Primary, @"
                                                                                                ")] + [InlineData(TypeColorLine.Secondary, @"
                                                                                                ")] + [InlineData(TypeColorLine.Info, @"
                                                                                                ")] + [InlineData(TypeColorLine.Warning, @"
                                                                                                ")] + [InlineData(TypeColorLine.Danger, @"
                                                                                                ")] + [InlineData(TypeColorLine.Light, @"
                                                                                                ")] + [InlineData(TypeColorLine.Dark, @"
                                                                                                ")] + public void Color(TypeColorLine color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLine() + { + Color = new PropertyColorLine(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLink.cs new file mode 100644 index 00000000..91b31bf0 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLink.cs @@ -0,0 +1,222 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlLink + { + /// + /// Tests the id property of the link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title property of the link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the target property of the link control. + /// + [Theory] + [InlineData(TypeTarget.None, @"")] + [InlineData(TypeTarget.Blank, @"")] + [InlineData(TypeTarget.Self, @"")] + [InlineData(TypeTarget.Parent, @"")] + [InlineData(TypeTarget.Framename, @"")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                                                                                                b", @"b"" data-bs-toggle=""tooltip"">")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the add function of the link control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlLink(null); + var control5 = new ControlLink(null); + var control6 = new ControlLink(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + Assert.Equal(@"", html4.Trim()); + Assert.Equal(@"", html5.Trim()); + Assert.Equal(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLinkList.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLinkList.cs new file mode 100644 index 00000000..9a54f8ed --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlLinkList.cs @@ -0,0 +1,167 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the link list control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlLinkList + { + /// + /// Tests the id property of the link list control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("id", @"
                                                                                                ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLinkList(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name property of the link list control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData("abc", @"
                                                                                                abc
                                                                                                ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                                WebExpress.WebUI
                                                                                                ")] + public void Name(string name, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLinkList() + { + Name = name, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the name color property of the link list control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Primary, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Secondary, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Info, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Success, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Warning, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Danger, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Light, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Dark, @"
                                                                                                abc
                                                                                                ")] + [InlineData(TypeColorText.Muted, @"
                                                                                                abc
                                                                                                ")] + public void NameColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLinkList() + { + Name = "abc", + NameColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the link list control. + /// + [Theory] + [InlineData(null, @"
                                                                                                ")] + [InlineData(typeof(IconStar), @"
                                                                                                ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLinkList() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the link list control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Info, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                ")] + [InlineData(TypeColorBackground.White, @"
                                                                                                ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLinkList() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the link list control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLinkList(); + + // test execution + control.Add(new ControlLink() { Text = "abc" }); + + var html = control.Render(context, visualTree); + + Assert.Equal(@"", html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlList.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlList.cs new file mode 100644 index 00000000..5e8d409c --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlList.cs @@ -0,0 +1,124 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the list control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlList + { + /// + /// Tests the id property of the list control. + /// + [Theory] + [InlineData(null, @"
                                                                                                  ")] + [InlineData("id", @"
                                                                                                    ")] + [InlineData("03C6031F-04A9-451F-B817-EBD6D32F8B0C", @"
                                                                                                      ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlList(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the list control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                        ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                          ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                            ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                              ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                  ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                      ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlList() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the list control. + /// + [Theory] + [InlineData(TypeLayoutList.Default, @"
                                                                                                                        ")] + [InlineData(TypeLayoutList.Simple, @"
                                                                                                                          ")] + [InlineData(TypeLayoutList.Group, @"
                                                                                                                            ")] + [InlineData(TypeLayoutList.Horizontal, @"
                                                                                                                              ")] + [InlineData(TypeLayoutList.Flush, @"
                                                                                                                                ")] + public void Layout(TypeLayoutList layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlList() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the list control. + /// + [Theory] + [MemberData(nameof(GetControlListItemsData))] + public void Add(IEnumerable items, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlList(null, items.ToArray()); + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Provides test data for the Add method of the UnitTestControlList class. + /// + /// An enumerable collection of object arrays, each containing test data. + public static TheoryData, string> GetControlListItemsData() + { + return new TheoryData, string> + { + { new List { new(null, new ControlText() { Text = "Item 1" }) }, @"
                                                                                                                                • Item 1
                                                                                                                                " }, + { new List { new("id") }, @"
                                                                                                                                " }, + { new List { }, "
                                                                                                                                  " } + }; + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlListItemButton.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlListItemButton.cs new file mode 100644 index 00000000..aedec955 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlListItemButton.cs @@ -0,0 +1,96 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the list item button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlListItemButton + { + /// + /// Tests the id property of the list item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemButton(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Active property of the list item button control. + /// + [Theory] + [InlineData(TypeActive.None, @"")] + [InlineData(TypeActive.Active, @"")] + public void Active(TypeActive active, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemButton(null) + { + Active = active + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the list item button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlListItemButton(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlListItemButton(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlListItemButton(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlListItemButton(null); + var control5 = new ControlListItemButton(null); + var control6 = new ControlListItemButton(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html1); + AssertExtensions.EqualWithPlaceholders(@"", html2); + AssertExtensions.EqualWithPlaceholders(@"", html3); + AssertExtensions.EqualWithPlaceholders(@"", html4); + AssertExtensions.EqualWithPlaceholders(@"", html5); + AssertExtensions.EqualWithPlaceholders(@"", html6); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlListItemLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlListItemLink.cs new file mode 100644 index 00000000..5d81b591 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlListItemLink.cs @@ -0,0 +1,245 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the list item link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlListItemLink + { + /// + /// Tests the id property of the list item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the list item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the list item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title property of the list item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the target property of the list item link control. + /// + [Theory] + [InlineData(TypeTarget.None, @"")] + [InlineData(TypeTarget.Blank, @"")] + [InlineData(TypeTarget.Self, @"")] + [InlineData(TypeTarget.Parent, @"")] + [InlineData(TypeTarget.Framename, @"")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the list item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                                                                                                                                  b", @"")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the list item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the Active property of the list item link control. + /// + [Theory] + [InlineData(TypeActive.None, @"")] + [InlineData(TypeActive.Active, @"")] + public void Active(TypeActive active, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlListItemLink(null) + { + Active = active + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the list item link control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlListItemLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlListItemLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlListItemLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlListItemLink(null); + var control5 = new ControlListItemLink(null); + var control6 = new ControlListItemLink(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html1); + AssertExtensions.EqualWithPlaceholders(@"", html2); + AssertExtensions.EqualWithPlaceholders(@"", html3); + AssertExtensions.EqualWithPlaceholders(@"", html4); + AssertExtensions.EqualWithPlaceholders(@"", html5); + AssertExtensions.EqualWithPlaceholders(@"", html6); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlModal.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlModal.cs new file mode 100644 index 00000000..654eec52 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlModal.cs @@ -0,0 +1,59 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the modal control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlModal + { + /// + /// Tests the id property of the modal control. + /// + [Theory] + [InlineData(null, @"

                                                                                                                                  ")] + [InlineData("id", @"

                                                                                                                                  ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlModal(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the header property of the modal control. + /// + [Theory] + [InlineData(null, @"

                                                                                                                                  ")] + [InlineData("abc", @"

                                                                                                                                  abc

                                                                                                                                  ")] + + public void Header(string header, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlModal() + { + Header = header + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlModalForm.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlModalForm.cs new file mode 100644 index 00000000..9b397feb --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlModalForm.cs @@ -0,0 +1,80 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the modal control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlModalForm + { + /// + /// Tests the id property of the modal control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                  *
                                                                                                                                  ")] + [InlineData("id", @"
                                                                                                                                  *
                                                                                                                                  ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlModalForm(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the header property of the modal control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                  *
                                                                                                                                  ")] + [InlineData("abc", @"
                                                                                                                                  *

                                                                                                                                  abc

                                                                                                                                  *
                                                                                                                                  ")] + + public void Header(string header, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlModalForm() + { + Header = header + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the modal control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlModalForm() + { + }; + + // test execution + control.Add(new ControlFormItemInputTextBox() { Value = "abc" }); + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                  **
                                                                                                                                  ", html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlMultipleProgressBar.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlMultipleProgressBar.cs new file mode 100644 index 00000000..b9e62a3a --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlMultipleProgressBar.cs @@ -0,0 +1,170 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the multiple progress bar control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlMultipleProgressBar + { + /// + /// Tests the id property of the multiple progress bar control. + /// + [Theory] + [InlineData(null, @"0%")] + [InlineData("id", @"0%")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlMultipleProgressBar(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the format property of the multiple progress bar control. + /// + [Theory] + [InlineData(TypeFormatProgress.Default, @"0%")] + [InlineData(TypeFormatProgress.Colored, @"
                                                                                                                                  ")] + [InlineData(TypeFormatProgress.Striped, @"
                                                                                                                                  ")] + [InlineData(TypeFormatProgress.Animated, @"
                                                                                                                                  ")] + public void Format(TypeFormatProgress format, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlMultipleProgressBar() + { + Format = format + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the multiple progress bar control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                  ")] + [InlineData("abc", @"
                                                                                                                                  abc
                                                                                                                                  ")] + [InlineData("webexpress.webui:plugin.name", @"
                                                                                                                                  WebExpress.WebUI
                                                                                                                                  ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var item = new ControlMultipleProgressBarItem() { Text = text }; + var control = new ControlMultipleProgressBar(null, item) + { + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the color property of the multiple progress bar control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                                                                                                                                  *
                                                                                                                                  ")] + [InlineData(TypeColorText.Primary, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Secondary, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Info, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Warning, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Danger, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Light, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.White, @"
                                                                                                                                  ")] + public void Color(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var item = new ControlMultipleProgressBarItem() { Color = new PropertyColorText(color) }; + var control = new ControlMultipleProgressBar(null, item) + { + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the multiple progress bar control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Primary, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Secondary, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Info, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Warning, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Danger, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Light, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.White, @"
                                                                                                                                  ")] + [InlineData(TypeColorText.Muted, @"
                                                                                                                                  ")] + public void TextColor(TypeColorText textColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlMultipleProgressBar() + { + TextColor = new PropertyColorText(textColor), + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form multiple progress bar control. + /// + [Theory] + [InlineData(0u, @"0%")] + [InlineData(100u, @"100%")] + public void Value(uint value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var item = new ControlMultipleProgressBarItem() { Value = value }; + var control = new ControlMultipleProgressBar(null, item) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigation.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigation.cs new file mode 100644 index 00000000..1224e7d3 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigation.cs @@ -0,0 +1,109 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the navigation control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlNavigation + { + /// + /// Tests the id property of the navigation control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                      ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigation(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the navigation control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                        ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                          ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                            ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                              ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                  ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                      ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigation() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the navigation control. + /// + [Theory] + [InlineData(TypeLayoutTab.Default, @"
                                                                                                                                                        ")] + [InlineData(TypeLayoutTab.Menu, @"
                                                                                                                                                          ")] + [InlineData(TypeLayoutTab.Tab, @"
                                                                                                                                                            ")] + [InlineData(TypeLayoutTab.Pill, @"
                                                                                                                                                              ")] + public void Layout(TypeLayoutTab layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigation() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the navigation control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigation(); + + // test execution + control.Add(new ControlNavigationItemLink() { Text = "abc" }); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemDropdown.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemDropdown.cs new file mode 100644 index 00000000..0fa33620 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemDropdown.cs @@ -0,0 +1,322 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the navigation item dropdown control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlNavigationItemDropdown + { + /// + /// Tests the id property of the navigation item dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                ")] + [InlineData("id", @"
                                                                                                                                                                  ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the navigation item dropdown control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"
                                                                                                                                                                    ")] + [InlineData(TypeColorButton.Primary, @"
                                                                                                                                                                      ")] + [InlineData(TypeColorButton.Secondary, @"
                                                                                                                                                                        ")] + [InlineData(TypeColorButton.Info, @"
                                                                                                                                                                          ")] + [InlineData(TypeColorButton.Warning, @"
                                                                                                                                                                            ")] + [InlineData(TypeColorButton.Danger, @"
                                                                                                                                                                              ")] + [InlineData(TypeColorButton.Light, @"
                                                                                                                                                                                ")] + [InlineData(TypeColorButton.Dark, @"
                                                                                                                                                                                  ")] + public void BackgroundColor(TypeColorButton backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + BackgroundColor = new PropertyColorButton(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the navigation item dropdown control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"
                                                                                                                                                                                    ")] + [InlineData(TypeSizeButton.Small, @"
                                                                                                                                                                                      ")] + [InlineData(TypeSizeButton.Large, @"
                                                                                                                                                                                        ")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the navigation item dropdown control. + /// + [Theory] + [InlineData(false, @"
                                                                                                                                                                                          ")] + [InlineData(true, @"
                                                                                                                                                                                            ")] + public void Outline(bool outline, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Outline = outline, + BackgroundColor = new PropertyColorButton(TypeColorButton.Primary) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the navigation item dropdown control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"
                                                                                                                                                                                              ")] + [InlineData(TypeBlockButton.Block, @"
                                                                                                                                                                                                ")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the toogle property of the navigation item dropdown control. + /// + [Theory] + [InlineData(TypeToggleDropdown.None, @"
                                                                                                                                                                                                  ")] + [InlineData(TypeToggleDropdown.Toggle, @"
                                                                                                                                                                                                    ")] + public void Toogle(TypeToggleDropdown toogle, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Toogle = toogle + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the navigation item dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                      ")] + [InlineData("abc", @"
                                                                                                                                                                                                        ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                                                                                                                                          ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the navigation item dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                            ")] + [InlineData("abc", @"
                                                                                                                                                                                                              ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                                                                                                                                                ")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Tooltip = tooltip, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the navigation item dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                  ")] + [InlineData("abc", @"
                                                                                                                                                                                                                    ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                                                                                                                                                      ")] + public void Value(string value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Value = value, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the icon property of the navigation item dropdown control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                        ")] + [InlineData(typeof(IconStar), @"
                                                                                                                                                                                                                          ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the active property of the navigation item dropdown control. + /// + [Theory] + [InlineData(TypeActive.None, @"
                                                                                                                                                                                                                            ")] + [InlineData(TypeActive.Active, @"
                                                                                                                                                                                                                              ")] + [InlineData(TypeActive.Disabled, @"
                                                                                                                                                                                                                                ")] + public void Active(TypeActive active, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + Active = active + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the alignment menu property of the navigation item dropdown control. + /// + [Theory] + [InlineData(TypeAlignmentDropdownMenu.Default, @"
                                                                                                                                                                                                                                  ")] + [InlineData(TypeAlignmentDropdownMenu.Right, @"
                                                                                                                                                                                                                                    ")] + public void AlignmentMenu(TypeAlignmentDropdownMenu alignmentMenu, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown() + { + AlignmentMenu = alignmentMenu + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the navigation item dropdown control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemDropdown(); + + // test execution + control.Add(new ControlDropdownItemLink() { Text = "abc" }); + + var html = control.Render(context, visualTree); + + Assert.Equal(@"", html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemHeader.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemHeader.cs new file mode 100644 index 00000000..6bed6e11 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemHeader.cs @@ -0,0 +1,59 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the navigation item header control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlNavigationItemHeader + { + /// + /// Tests the id property of the navigation item header control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                  • ")] + [InlineData("id", @"
                                                                                                                                                                                                                                  • ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemHeader(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the navigation item header control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                  • ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                  • abc
                                                                                                                                                                                                                                  • ")] + [InlineData("webexpress.WebUI:plugin.name", @"
                                                                                                                                                                                                                                  • WebExpress.WebUI
                                                                                                                                                                                                                                  • ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemHeader() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemLink.cs new file mode 100644 index 00000000..72e98edc --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlNavigationItemLink.cs @@ -0,0 +1,232 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the navigation item link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlNavigationItemLink + { + /// + /// Tests the id property of the navigation item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the text property of the navigation item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the uri property of the navigation item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the title property of the navigation item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the target property of the navigation item link control. + /// + [Theory] + [InlineData(TypeTarget.None, @"")] + [InlineData(TypeTarget.Blank, @"")] + [InlineData(TypeTarget.Self, @"")] + [InlineData(TypeTarget.Parent, @"")] + [InlineData(TypeTarget.Framename, @"")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the tooltip property of the navigation item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                                                                                                                                                                                                                                    b", @"b"" data-bs-toggle=""tooltip"">")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the icon property of the navigation item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the no wrap property of the navigation item link control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void NoWrap(bool noWrap, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlNavigationItemLink() + { + NoWrap = noWrap + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the content property of the navigation item link control. + /// + [Fact] + public void Content() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlNavigationItemLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlNavigationItemLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlNavigationItemLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + // test execution + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPagination.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPagination.cs new file mode 100644 index 00000000..e5ca1f5e --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPagination.cs @@ -0,0 +1,36 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the pagination control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPagination + { + /// + /// Tests the id property of the pagination control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPagination(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanel.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanel.cs new file mode 100644 index 00000000..be03217c --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanel.cs @@ -0,0 +1,138 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanel + { + /// + /// Tests the id property of the panel control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanel(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the panel control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                    ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanel() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the panel control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanel() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the panel control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanel() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanel(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCallout.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCallout.cs new file mode 100644 index 00000000..f5743f66 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCallout.cs @@ -0,0 +1,109 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel callout control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelCallout + { + /// + /// Tests the id property of the panel callout control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCallout(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the panel callout control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCallout() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the panel callout control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCallout() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel callout control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelCallout(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCard.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCard.cs new file mode 100644 index 00000000..ebc62114 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCard.cs @@ -0,0 +1,150 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel card control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelCard + { + /// + /// Tests the id property of the panel card control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCard(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the header property of the panel card control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("header", @"
                                                                                                                                                                                                                                    header
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void Header(string header, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCard() + { + Header = header + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the header image property of the panel card control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("headerImage", @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void HeaderImage(string headerImage, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCard() + { + HeaderImage = headerImage + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the headline property of the panel card control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("headline", @"
                                                                                                                                                                                                                                    *

                                                                                                                                                                                                                                    headline

                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void Headline(string headline, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCard() + { + Headline = headline + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the footer property of the panel card control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("footer", @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    footer
                                                                                                                                                                                                                                    ")] + public void Footer(string footer, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCard() + { + Footer = footer + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the footer image property of the panel card control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("footerImage", @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void FooterImage(string footerImage, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCard() + { + FooterImage = footerImage + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCenter.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCenter.cs new file mode 100644 index 00000000..bcd56157 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelCenter.cs @@ -0,0 +1,138 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the center panel control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelCenter + { + /// + /// Tests the id property of the center panel control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCenter(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the center panel control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                    ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCenter() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the center panel control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCenter() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the center panel control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelCenter() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the center panel control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelCenter(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelFlexbox.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelFlexbox.cs new file mode 100644 index 00000000..37dc14d2 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelFlexbox.cs @@ -0,0 +1,210 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel flexbox control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelFlexbox + { + /// + /// Tests the id property of the panel flexbox control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the panel flexbox control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the panel flexbox control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the panel flexbox control. + /// + [Theory] + [InlineData(TypeLayoutFlexbox.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeLayoutFlexbox.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeLayoutFlexbox.Inline, @"
                                                                                                                                                                                                                                    ")] + public void Layout(TypeLayoutFlexbox layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox() + { + Layout = layout, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the justify property of the panel flexbox control. + /// + [Theory] + [InlineData(TypeJustifiedFlexbox.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeJustifiedFlexbox.Start, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeJustifiedFlexbox.Around, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeJustifiedFlexbox.Between, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeJustifiedFlexbox.End, @"
                                                                                                                                                                                                                                    ")] + public void Justify(TypeJustifiedFlexbox justify, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox() + { + Justify = justify, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the align property of the panel flexbox control. + /// + [Theory] + [InlineData(TypeAlignFlexbox.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeAlignFlexbox.Start, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeAlignFlexbox.Stretch, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeAlignFlexbox.Baseline, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeAlignFlexbox.End, @"
                                                                                                                                                                                                                                    ")] + public void Align(TypeAlignFlexbox align, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox() + { + Align = align, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the wrap property of the panel flexbox control. + /// + [Theory] + [InlineData(TypeWrap.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeWrap.Wrap, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeWrap.Nowrap, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeWrap.Reverse, @"
                                                                                                                                                                                                                                    ")] + public void Wrap(TypeWrap wrap, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFlexbox() + { + Wrap = wrap, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel flexbox control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelFlexbox(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelFooter.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelFooter.cs new file mode 100644 index 00000000..d48e3c6d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelFooter.cs @@ -0,0 +1,109 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel footer control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelFooter + { + /// + /// Tests the id property of the panel footer control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFooter(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the panel footer control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFooter() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the panel footer control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelFooter() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel footer control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelFooter(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelGrid.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelGrid.cs new file mode 100644 index 00000000..d0544d5d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelGrid.cs @@ -0,0 +1,138 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the grid control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelGrid + { + /// + /// Tests the id property of the grid control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelGrid(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the grid control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                    ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelGrid() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the grid control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelGrid() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the grid control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelGrid() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the grid control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelGrid(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelHeader.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelHeader.cs new file mode 100644 index 00000000..9fadd120 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelHeader.cs @@ -0,0 +1,156 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel header control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelHeader + { + /// + /// Tests the id property of the panel header control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelHeader(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the panel header control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelHeader() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the panel header control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelHeader() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fixed property of the panel header control. + /// + [Theory] + [InlineData(TypeFixed.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeFixed.Top, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeFixed.Bottom, @"
                                                                                                                                                                                                                                    ")] + public void Fixed(TypeFixed fixedValue, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelHeader() + { + Fixed = fixedValue + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the sticky property of the panel header control. + /// + [Theory] + [InlineData(TypeSticky.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeSticky.Top, @"
                                                                                                                                                                                                                                    ")] + public void Sticky(TypeSticky sticky, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelHeader() + { + Sticky = sticky, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel header control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelHeader(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelMain.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelMain.cs new file mode 100644 index 00000000..f5d2a4e2 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelMain.cs @@ -0,0 +1,109 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel main control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelMain + { + /// + /// Tests the id property of the panel main control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMain(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the panel main control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMain() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the panel main control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMain() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel main control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelMain(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelMedia.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelMedia.cs new file mode 100644 index 00000000..8352647f --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelMedia.cs @@ -0,0 +1,231 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the media control panel. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelMedia + { + /// + /// Tests the id property of the media control panel. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the media control panel. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                    ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the media control panel. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the media control panel. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title property of the media control panel. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("Title", @"

                                                                                                                                                                                                                                    Title

                                                                                                                                                                                                                                    ")] + [InlineData("webexpress.webui:plugin.name", @"

                                                                                                                                                                                                                                    WebExpress.WebUI

                                                                                                                                                                                                                                    ")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + Title = title + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the image property of the media control panel. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("image.jpg", @"
                                                                                                                                                                                                                                    ")] + public void Image(string image, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + Image = image + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the image width property of the media control panel. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData(100u, @"
                                                                                                                                                                                                                                    ")] + public void ImageWidth(uint? imageWidth, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + ImageWidth = imageWidth + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the image height property of the media control panel. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData(100u, @"
                                                                                                                                                                                                                                    ")] + public void ImageHeight(uint? imageHeight, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelMedia() + { + ImageHeight = imageHeight + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the media control panel. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelMedia(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelNavbar.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelNavbar.cs new file mode 100644 index 00000000..51d93a1a --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelNavbar.cs @@ -0,0 +1,138 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the navbar control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelNavbar + { + /// + /// Tests the id property of the navbar control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelNavbar(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the navbar control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelNavbar() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the navbar control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"")] + [InlineData(TypeDirection.Vertical, @"")] + [InlineData(TypeDirection.VerticalReverse, @"")] + [InlineData(TypeDirection.Horizontal, @"")] + [InlineData(TypeDirection.HorizontalReverse, @"")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelNavbar() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the navbar control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"")] + [InlineData(TypePanelContainer.Default, @"")] + [InlineData(TypePanelContainer.Fluid, @"")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelNavbar() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the navbar control. + /// + [Theory] + [InlineData(typeof(ControlText), @"")] + [InlineData(typeof(ControlLink), @"")] + [InlineData(typeof(ControlImage), @"")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelNavbar(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelSplit.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelSplit.cs new file mode 100644 index 00000000..179a3934 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelSplit.cs @@ -0,0 +1,127 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the panel split control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelSplit + { + /// + /// Tests the id property of the panel split control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelSplit(id); + control.AddPanel1(new ControlText() { Text = "p1" }); + control.AddPanel2(new ControlText() { Text = "p2" }); + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the orientation property of the panel split control. + /// + [Theory] + [InlineData(TypeOrientationSplit.Horizontal, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeOrientationSplit.Vertical, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void Orientation(TypeOrientationSplit orientation, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelSplit() + { + Orientation = orientation, + }; + control.AddPanel1(new ControlText() { Text = "p1" }); + control.AddPanel2(new ControlText() { Text = "p2" }); + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the splitter color property of the panel split control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                    *
                                                                                                                                                                                                                                    ")] + public void SplitterColor(TypeColorBackground splitterColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelSplit() + { + SplitterColor = new PropertyColorBackground(splitterColor), + }; + control.AddPanel1(new ControlText() { Text = "p1" }); + control.AddPanel2(new ControlText() { Text = "p2" }); + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the panel split control. + /// + [Theory] + [InlineData(null, null, null)] + [InlineData(typeof(ControlText), null, @"
                                                                                                                                                                                                                                    ")] + [InlineData(null, typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlText), typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child1, Type child2, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance1 = child1 != null ? Activator.CreateInstance(child1, [null]) as IControl : null; + var childInstance2 = child2 != null ? Activator.CreateInstance(child2, [null]) as IControl : null; + var control = new ControlPanelSplit(); + + // test execution + if (childInstance1 != null) + { + control.AddPanel1(childInstance1); + } + + if (childInstance2 != null) + { + control.AddPanel2(childInstance2); + } + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelToast.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelToast.cs new file mode 100644 index 00000000..c3462ff4 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelToast.cs @@ -0,0 +1,138 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the toast control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelToast + { + /// + /// Tests the id property of the toast control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                    ")] + [InlineData("id", @"
                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelToast(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the toast control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                    ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelToast() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the toast control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                    ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelToast() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the toast control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                    ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                    ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelToast() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the toast control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                    ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                    ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelToast(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelTool.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelTool.cs new file mode 100644 index 00000000..8d8705fc --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlPanelTool.cs @@ -0,0 +1,134 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the tool panel control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlPanelTool + { + /// + /// Tests the id property of the tool panel control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                      ")] + [InlineData("id", @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelTool(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the direction property of the tool panel control. + /// + [Theory] + [InlineData(TypeDirection.Default, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeDirection.Vertical, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeDirection.VerticalReverse, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeDirection.Horizontal, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeDirection.HorizontalReverse, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + public void Direction(TypeDirection direction, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelTool() + { + Direction = direction, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the fluid property of the tool panel control. + /// + [Theory] + [InlineData(TypePanelContainer.None, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypePanelContainer.Default, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypePanelContainer.Fluid, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + public void Fluid(TypePanelContainer fluid, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlPanelTool() + { + Fluid = fluid, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tools property of the tool panel control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                      *abc*
                                                                                                                                                                                                                                      ")] + [InlineData("webexpress.webui:plugin.name", @"
                                                                                                                                                                                                                                      ")] + public void Tools(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = new ControlDropdownItemLink() { Text = text }; + var control = new ControlPanelTool() + { + }; + + // test execution + control.Tools.Add(childInstance); + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the tool panel control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(typeof(ControlLink), @"
                                                                                                                                                                                                                                      **
                                                                                                                                                                                                                                      ")] + [InlineData(typeof(ControlImage), @"
                                                                                                                                                                                                                                      **
                                                                                                                                                                                                                                      ")] + public void Add(Type child, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(child, [null]) as IControl; + var control = new ControlPanelTool(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlProgressBar.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlProgressBar.cs new file mode 100644 index 00000000..6d985617 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlProgressBar.cs @@ -0,0 +1,245 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the progress bar control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlProgressBar + { + /// + /// Tests the id property of the progress bar control. + /// + [Theory] + [InlineData(null, @"0%")] + [InlineData("id", @"0%")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the format property of the progress bar control. + /// + [Theory] + [InlineData(TypeFormatProgress.Default, @"0%")] + [InlineData(TypeFormatProgress.Colored, @"
                                                                                                                                                                                                                                      ")] + [InlineData(TypeFormatProgress.Striped, @"
                                                                                                                                                                                                                                      ")] + [InlineData(TypeFormatProgress.Animated, @"
                                                                                                                                                                                                                                      ")] + public void Format(TypeFormatProgress format, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Format = format + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the progress bar control. + /// + [Theory] + [InlineData(TypeSizeProgress.Default, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeSizeProgress.ExtraSmall, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeSizeProgress.Small, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeSizeProgress.Large, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeSizeProgress.ExtraLarge, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + public void Size(TypeSizeProgress size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Size = size, + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the progress bar control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                      ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                      abc
                                                                                                                                                                                                                                      ")] + [InlineData("webexpress.webui:plugin.name", @"
                                                                                                                                                                                                                                      WebExpress.WebUI
                                                                                                                                                                                                                                      ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Text = text, + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the color property of the progress bar control. + /// + [Theory] + [InlineData(TypeColorProgress.Default, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeColorProgress.Primary, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-primary"" */div>")] + [InlineData(TypeColorProgress.Secondary, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-secondary"" */div>")] + [InlineData(TypeColorProgress.Info, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-info"" */div>")] + [InlineData(TypeColorProgress.Warning, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-warning"" */div>")] + [InlineData(TypeColorProgress.Danger, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-danger"" */div>")] + [InlineData(TypeColorProgress.Light, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-light"" */div>")] + [InlineData(TypeColorProgress.White, @"
                                                                                                                                                                                                                                      <* class=""progress-bar bg-white"" */div>")] + public void Color(TypeColorProgress color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Color = new PropertyColorProgress(color), + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the progress bar control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                                                                                                                                                                                                                                      *
                                                                                                                                                                                                                                      ")] + [InlineData(TypeColorText.Primary, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-primary"" */div>")] + [InlineData(TypeColorText.Secondary, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-secondary"" */div>")] + [InlineData(TypeColorText.Info, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-info"" */div>")] + [InlineData(TypeColorText.Warning, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-warning"" */div>")] + [InlineData(TypeColorText.Danger, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-danger"" */div>")] + [InlineData(TypeColorText.Light, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-light"" */div>")] + [InlineData(TypeColorText.White, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-white"" */div>")] + [InlineData(TypeColorText.Muted, @"
                                                                                                                                                                                                                                      <* class=""progress-bar text-muted"" */div>")] + public void TextColor(TypeColorText textColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + TextColor = new PropertyColorText(textColor), + Format = TypeFormatProgress.Colored + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the min property of the form progress bar control. + /// + [Theory] + [InlineData(0u, @"0%")] + [InlineData(100u, @"0%")] + public void Min(uint min, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Min = min + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the max property of the form progress bar control. + /// + [Theory] + [InlineData(0u, @"0%")] + [InlineData(100u, @"0%")] + public void Max(uint max, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Max = max + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the value property of the form progress bar control. + /// + [Theory] + [InlineData(0u, @"0%")] + [InlineData(100u, @"100%")] + public void Value(uint value, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var form = new ControlForm(); + var context = new RenderControlFormContext(UnitTestControlFixture.CrerateRenderContextMock(), form); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlProgressBar() + { + Value = value + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButton.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButton.cs new file mode 100644 index 00000000..b45856ad --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButton.cs @@ -0,0 +1,224 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the split button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlSplitButton + { + /// + /// Tests the id property of the split button control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                        ")] + [InlineData("id", @"
                                                                                                                                                                                                                                          ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButton(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the split button control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButton() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the split button control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData(TypeSizeButton.Small, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData(TypeColorButton.Primary, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData(true, TypeColorButton.Default, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData(true, TypeColorButton.Primary, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData(TypeBlockButton.Block, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButton() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the split button control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + [InlineData(typeof(IconStar), @"
                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                          ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButton() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the split button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlSplitButton(null, new ControlSplitButtonItemLink() { Text = "abc" }); + var control2 = new ControlSplitButton(null, [new ControlSplitButtonItemLink() { Text = "abc" }]); + var control3 = new ControlSplitButton(null, new List([new ControlSplitButtonItemLink() { Text = "abc" }]).ToArray()); + var control4 = new ControlSplitButton(null); + var control5 = new ControlSplitButton(null); + var control6 = new ControlSplitButton(null); + + // test execution + control4.Add(new ControlSplitButtonItemLink() { Text = "abc" }); + control5.Add([new ControlSplitButtonItemLink() { Text = "abc" }]); + control6.Add(new List([new ControlSplitButtonItemLink() { Text = "abc" }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + var expected = @"
                                                                                                                                                                                                                                          *abc*
                                                                                                                                                                                                                                          "; + AssertExtensions.EqualWithPlaceholders(expected, html1); + AssertExtensions.EqualWithPlaceholders(expected, html2); + AssertExtensions.EqualWithPlaceholders(expected, html3); + AssertExtensions.EqualWithPlaceholders(expected, html4); + AssertExtensions.EqualWithPlaceholders(expected, html5); + AssertExtensions.EqualWithPlaceholders(expected, html6); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemDivider.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemDivider.cs new file mode 100644 index 00000000..32dd3f6d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemDivider.cs @@ -0,0 +1,35 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the split button item divider control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlSplitButtonItemDivider + { + /// + /// Tests the id property of the split button item divider control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                          ")] + [InlineData("id", @"
                                                                                                                                                                                                                                          ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemDivider(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemHeader.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemHeader.cs new file mode 100644 index 00000000..0fbfad7d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemHeader.cs @@ -0,0 +1,59 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the split button item header control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlSplitButtonItemHeader + { + /// + /// Tests the id property of the split button item header control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                        • ")] + [InlineData("id", @"
                                                                                                                                                                                                                                        • ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemHeader(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the split button item header control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                        • ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                        • abc
                                                                                                                                                                                                                                        • ")] + [InlineData("webexpress.webui:plugin.name", @"
                                                                                                                                                                                                                                        • WebExpress.WebUI
                                                                                                                                                                                                                                        • ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemHeader() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemLink.cs new file mode 100644 index 00000000..261d4375 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonItemLink.cs @@ -0,0 +1,222 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the split button item link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlSplitButtonItemLink + { + /// + /// Tests the id property of the split button item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the text property of the split button item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the uri property of the split button item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the title property of the split button item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the target property of the split button item link control. + /// + [Theory] + [InlineData(TypeTarget.None, @"")] + [InlineData(TypeTarget.Blank, @"")] + [InlineData(TypeTarget.Self, @"")] + [InlineData(TypeTarget.Parent, @"")] + [InlineData(TypeTarget.Framename, @"")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the tooltip property of the split button item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + [InlineData("a
                                                                                                                                                                                                                                          b", @"b"" data-bs-toggle=""tooltip"">")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the icon property of the split button item link control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonItemLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html.Trim()); + } + + /// + /// Tests the add function of the split button item link control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlLink(null); + var control5 = new ControlLink(null); + var control6 = new ControlLink(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + Assert.Equal(@"", html4.Trim()); + Assert.Equal(@"", html5.Trim()); + Assert.Equal(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonLink.cs new file mode 100644 index 00000000..b10da38e --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlSplitButtonLink.cs @@ -0,0 +1,224 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the split button link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlSplitButtonLink + { + /// + /// Tests the id property of the split button link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                            ")] + [InlineData("id", @"
                                                                                                                                                                                                                                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the split button link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData("abc", @"")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the size property of the split button link control. + /// + [Theory] + [InlineData(TypeSizeButton.Default, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(TypeSizeButton.Small, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(TypeSizeButton.Large, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + public void Size(TypeSizeButton size, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink() + { + Size = size + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the backgroundcolor property of the split button link control. + /// + [Theory] + [InlineData(TypeColorButton.Default, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorButton.Primary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorButton.Secondary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorButton.Warning, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorButton.Danger, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorButton.Dark, @"
                                                                                                                                                                                                                                              ")] + public void BackgroundColor(TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink() + { + BackgroundColor = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the outline property of the split button link control. + /// + [Theory] + [InlineData(false, TypeColorButton.Default, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(true, TypeColorButton.Default, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(true, TypeColorButton.Primary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(true, TypeColorButton.Secondary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(true, TypeColorButton.Warning, @"
                                                                                                                                                                                                                                              ")] + [InlineData(true, TypeColorButton.Danger, @"
                                                                                                                                                                                                                                              ")] + [InlineData(true, TypeColorButton.Dark, @"
                                                                                                                                                                                                                                              ")] + public void Outline(bool outline, TypeColorButton color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink() + { + Outline = outline, + BackgroundColor = new PropertyColorButton(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the block property of the split button link control. + /// + [Theory] + [InlineData(TypeBlockButton.None, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(TypeBlockButton.Block, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + public void Block(TypeBlockButton block, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink() + { + Block = block + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the split button link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlSplitButtonLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the split button link control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlSplitButtonLink(null, new ControlSplitButtonItemLink() { Text = "abc" }); + var control2 = new ControlSplitButtonLink(null, [new ControlSplitButtonItemLink() { Text = "abc" }]); + var control3 = new ControlSplitButtonLink(null, new List([new ControlSplitButtonItemLink() { Text = "abc" }]).ToArray()); + var control4 = new ControlSplitButtonLink(null); + var control5 = new ControlSplitButtonLink(null); + var control6 = new ControlSplitButtonLink(null); + + // test execution + control4.Add(new ControlSplitButtonItemLink() { Text = "abc" }); + control5.Add([new ControlSplitButtonItemLink() { Text = "abc" }]); + control6.Add(new List([new ControlSplitButtonItemLink() { Text = "abc" }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + var expected = @""; + AssertExtensions.EqualWithPlaceholders(expected, html1); + AssertExtensions.EqualWithPlaceholders(expected, html2); + AssertExtensions.EqualWithPlaceholders(expected, html3); + AssertExtensions.EqualWithPlaceholders(expected, html4); + AssertExtensions.EqualWithPlaceholders(expected, html5); + AssertExtensions.EqualWithPlaceholders(expected, html6); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTable.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTable.cs new file mode 100644 index 00000000..6a149b5d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTable.cs @@ -0,0 +1,134 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the table control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTable + { + /// + /// Tests the id property of the table control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              ")] + [InlineData("id", @"
                                                                                                                                                                                                                                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTable(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the column layout property of the table control. + /// + [Theory] + [InlineData(TypesLayoutTableRow.Default, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Primary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Secondary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Info, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Success, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Warning, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Danger, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Light, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Dark, @"
                                                                                                                                                                                                                                              ")] + public void ColumnLayout(TypesLayoutTableRow layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTable + { + ColumnLayout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the responsive property of the table control. + /// + [Theory] + [InlineData(false, @"
                                                                                                                                                                                                                                              ")] + [InlineData(true, @"
                                                                                                                                                                                                                                              ")] + public void Responsive(bool responsive, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTable + { + Responsive = responsive + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the striped property of the table control. + /// + [Theory] + [InlineData(true, @"
                                                                                                                                                                                                                                              ")] + [InlineData(false, @"
                                                                                                                                                                                                                                              ")] + public void Striped(bool striped, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTable + { + Striped = striped + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the reflow property of the table control. + /// + [Theory] + [InlineData(true, @"
                                                                                                                                                                                                                                              ")] + [InlineData(false, @"
                                                                                                                                                                                                                                              ")] + public void Reflow(bool reflow, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTable + { + Reflow = reflow + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTableColumn.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTableColumn.cs new file mode 100644 index 00000000..d828b5b6 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTableColumn.cs @@ -0,0 +1,114 @@ +ο»Ώusing WebExpress.WebCore.WebIcon; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the table column control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTableColumn + { + /// + /// Tests the id property of the table column control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              ")] + [InlineData("id", @"
                                                                                                                                                                                                                                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTableColumn(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the table column control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                              abc
                                                                                                                                                                                                                                              ")] + [InlineData("webexpress.webui:plugin.name", @"
                                                                                                                                                                                                                                              WebExpress.WebUI
                                                                                                                                                                                                                                              ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTableColumn() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the table column control. + /// + [Theory] + [InlineData(TypesLayoutTableRow.Default, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Primary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Secondary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Info, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Success, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Warning, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Danger, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Light, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypesLayoutTableRow.Dark, @"
                                                                                                                                                                                                                                              ")] + public void Layout(TypesLayoutTableRow layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTableColumn() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the table column control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              ")] + [InlineData(typeof(IconStar), @"
                                                                                                                                                                                                                                              ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTableColumn() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTableRow.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTableRow.cs new file mode 100644 index 00000000..50b91e10 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTableRow.cs @@ -0,0 +1,89 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the table row control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTableRow + { + /// + /// Tests the id property of the table row control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTableRow(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the table row control. + /// + [Theory] + [InlineData(TypesLayoutTableRow.Default, @"")] + [InlineData(TypesLayoutTableRow.Primary, @"")] + [InlineData(TypesLayoutTableRow.Secondary, @"")] + [InlineData(TypesLayoutTableRow.Info, @"")] + [InlineData(TypesLayoutTableRow.Success, @"")] + [InlineData(TypesLayoutTableRow.Warning, @"")] + [InlineData(TypesLayoutTableRow.Danger, @"")] + [InlineData(TypesLayoutTableRow.Light, @"")] + [InlineData(TypesLayoutTableRow.Dark, @"")] + public void Layout(TypesLayoutTableRow layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTableRow() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the table row control. + /// + [Theory] + [InlineData(typeof(ControlText), @"
                                                                                                                                                                                                                                              ")] + [InlineData(typeof(ControlLink), @"")] + [InlineData(typeof(ControlImage), @"")] + public void Add(Type cell, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var childInstance = Activator.CreateInstance(cell, [null]) as IControl; + var control = new ControlTableRow(); + + // test execution + control.Add(childInstance); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTag.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTag.cs new file mode 100644 index 00000000..a3a3904f --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTag.cs @@ -0,0 +1,112 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the tag control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTag + { + /// + /// Tests the id property of the tag control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTag(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the tag control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.webui:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTag() + { + Text = text + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the pill property of the tag control. + /// + [Theory] + [InlineData(false, @"")] + [InlineData(true, @"")] + public void Pill(bool pill, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTag() + { + Pill = pill + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the tag control. + /// + [Theory] + [InlineData(TypeColorBackgroundBadge.Default, @"")] + [InlineData(TypeColorBackgroundBadge.Primary, @"")] + [InlineData(TypeColorBackgroundBadge.Secondary, @"")] + [InlineData(TypeColorBackgroundBadge.Info, @"")] + [InlineData(TypeColorBackgroundBadge.Success, @"")] + [InlineData(TypeColorBackgroundBadge.Warning, @"")] + [InlineData(TypeColorBackgroundBadge.Danger, @"")] + [InlineData(TypeColorBackgroundBadge.Light, @"")] + [InlineData(TypeColorBackgroundBadge.Dark, @"")] + public void Layout(TypeColorBackgroundBadge layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTag() + { + Layout = new PropertyColorBackgroundBadge(layout) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlText.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlText.cs new file mode 100644 index 00000000..174864f4 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlText.cs @@ -0,0 +1,117 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the text control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlText + { + /// + /// Tests the id property of the text control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              ")] + [InlineData("id", @"
                                                                                                                                                                                                                                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlText(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the text control. + /// + [Theory] + [InlineData(null, TypeFormatText.Paragraph, @"

                                                                                                                                                                                                                                              ")] + [InlineData("abc", TypeFormatText.Paragraph, @"

                                                                                                                                                                                                                                              abc

                                                                                                                                                                                                                                              ")] + [InlineData("abc", TypeFormatText.Default, @"
                                                                                                                                                                                                                                              abc
                                                                                                                                                                                                                                              ")] + [InlineData("webexpress.webui:plugin.name", TypeFormatText.H1, @"

                                                                                                                                                                                                                                              WebExpress.WebUI

                                                                                                                                                                                                                                              ")] + public void Text(string text, TypeFormatText format, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlText() + { + Text = text, + Format = format + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text color property of the attribute control. + /// + [Theory] + [InlineData(TypeColorText.Default, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Primary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Secondary, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Info, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Success, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Warning, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Danger, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Light, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Dark, @"
                                                                                                                                                                                                                                              ")] + [InlineData(TypeColorText.Muted, @"
                                                                                                                                                                                                                                              ")] + public void TextColor(TypeColorText color, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + TextColor = new PropertyColorText(color) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the marging property of the attribute control. + /// + [Theory] + [InlineData(PropertySpacing.Space.Two, PropertySpacing.Space.Two, PropertySpacing.Space.Two, PropertySpacing.Space.Two, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(PropertySpacing.Space.One, PropertySpacing.Space.Two, PropertySpacing.Space.Three, PropertySpacing.Space.Four, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(PropertySpacing.Space.One, PropertySpacing.Space.None, PropertySpacing.Space.None, PropertySpacing.Space.None, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + [InlineData(PropertySpacing.Space.Two, PropertySpacing.Space.Auto, PropertySpacing.Space.Two, PropertySpacing.Space.None, @"
                                                                                                                                                                                                                                              *
                                                                                                                                                                                                                                              ")] + public void Marging(PropertySpacing.Space spaceLeft, PropertySpacing.Space spaceRight, PropertySpacing.Space spaceTop, PropertySpacing.Space spaceBottom, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlAttribute() + { + Margin = new PropertySpacingMargin(spaceLeft, spaceRight, spaceTop, spaceBottom) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolBarItemSeperator.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolBarItemSeperator.cs new file mode 100644 index 00000000..1f11d662 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolBarItemSeperator.cs @@ -0,0 +1,35 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the toolbar item divider control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlToolBarItemSeperator + { + /// + /// Tests the id property of the toolbar item divider control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                              ")] + [InlineData("id", @"
                                                                                                                                                                                                                                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemDivider(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolbar.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolbar.cs new file mode 100644 index 00000000..e346b23d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolbar.cs @@ -0,0 +1,84 @@ +ο»Ώ +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the toolbar control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlToolbar + { + /// + /// Tests the id property of the toolbar control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + [InlineData("03C6031F-04A9-451F-B817-EBD6D32F8B0C", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbar(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the toolbar control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbar() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the toolbar control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbar(null, new ControlToolbarItemButton() { Text = "abc" }); + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"", html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolbarItemButton.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolbarItemButton.cs new file mode 100644 index 00000000..6d4f9554 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlToolbarItemButton.cs @@ -0,0 +1,274 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the toolbar item button control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlToolbarItemButton + { + /// + /// Tests the id property of the toolbar item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("id", @"")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemButton(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the toolbar item button control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"")] + [InlineData(TypeColorBackground.Primary, @"")] + [InlineData(TypeColorBackground.Secondary, @"")] + [InlineData(TypeColorBackground.Warning, @"")] + [InlineData(TypeColorBackground.Danger, @"")] + [InlineData(TypeColorBackground.Dark, @"")] + [InlineData(TypeColorBackground.Light, @"")] + [InlineData(TypeColorBackground.Transparent, @"")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemButton() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the toolbar item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"abc")] + [InlineData("webexpress.WebUI:plugin.name", @"WebExpress.WebUI")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemButton() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the toolbar item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("abc", @"")] + [InlineData("webexpress.WebUI:plugin.name", @"")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemButton() + { + Tooltip = tooltip, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the toolbar item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData(typeof(IconStar), @"")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemButton() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the active property of the toolbar item button control. + /// + [Theory] + [InlineData(TypeActive.None, @"")] + [InlineData(TypeActive.Active, @"")] + [InlineData(TypeActive.Disabled, @"")] + public void Active(TypeActive active, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlToolbarItemButton() + { + Active = active + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the toolbar item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("/a", @"")] + [InlineData("/a/b", @"")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the title property of the toolbar item button control. + /// + [Theory] + [InlineData(null, @"")] + [InlineData("a", @"")] + [InlineData("b", @"")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the target property of the toolbar item button control. + /// + [Theory] + [InlineData(TypeTarget.None, @"")] + [InlineData(TypeTarget.Blank, @"")] + [InlineData(TypeTarget.Self, @"")] + [InlineData(TypeTarget.Parent, @"")] + [InlineData(TypeTarget.Framename, @"")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the add function of the toolbar item button control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlLink(null); + var control5 = new ControlLink(null); + var control6 = new ControlLink(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + Assert.Equal(@"", html1.Trim()); + Assert.Equal(@"", html2.Trim()); + Assert.Equal(@"", html3.Trim()); + Assert.Equal(@"", html4.Trim()); + Assert.Equal(@"", html5.Trim()); + Assert.Equal(@"", html6.Trim()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTree.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTree.cs new file mode 100644 index 00000000..cc04db9a --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTree.cs @@ -0,0 +1,120 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the tree control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTree : IClassFixture + { + /// + /// Tests the id property of the tree control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                ")] + [InlineData("id", @"
                                                                                                                                                                                                                                                  ")] + [InlineData("03C6031F-04A9-451F-B817-EBD6D32F8B0C", @"
                                                                                                                                                                                                                                                    ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTree(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the layout property of the tree control. + /// + [Theory] + [InlineData(TypeLayoutTree.Default, @"
                                                                                                                                                                                                                                                      ")] + [InlineData(TypeLayoutTree.Simple, @"
                                                                                                                                                                                                                                                        ")] + [InlineData(TypeLayoutTree.Group, @"
                                                                                                                                                                                                                                                          ")] + [InlineData(TypeLayoutTree.Horizontal, @"
                                                                                                                                                                                                                                                            ")] + [InlineData(TypeLayoutTree.Flat, @"
                                                                                                                                                                                                                                                              ")] + [InlineData(TypeLayoutTree.Flush, @"
                                                                                                                                                                                                                                                                ")] + [InlineData(TypeLayoutTree.TreeView, @"
                                                                                                                                                                                                                                                                  ")] + public void Layout(TypeLayoutTree layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTree() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the sorted property of the tree control. + /// + [Theory] + [InlineData(false, @"
                                                                                                                                                                                                                                                                    ")] + [InlineData(true, @"
                                                                                                                                                                                                                                                                      ")] + public void Sorted(bool sorted, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTree() + { + Sorted = sorted + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the add function of the tree control. + /// + [Theory] + [MemberData(nameof(GetControlTreeItemsData))] + public void Add(IEnumerable items, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + + // test execution + var control = new ControlTree(null, items.ToArray()); + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Provides test data for the Add method of the UnitTestControlList class. + /// + /// An enumerable collection of object arrays, each containing test data. + public static TheoryData, string> GetControlTreeItemsData() + { + return new TheoryData, string> + { + { new List { new(null, new ControlText() { Text = "Item 1" }) }, @"
                                                                                                                                                                                                                                                                      • Item 1
                                                                                                                                                                                                                                                                      " }, + { new List { new("id") }, @"
                                                                                                                                                                                                                                                                      " }, + { new List { }, "
                                                                                                                                                                                                                                                                        " } + }; + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTreeItem.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTreeItem.cs new file mode 100644 index 00000000..fa6bab2d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTreeItem.cs @@ -0,0 +1,156 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the tree item control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTreeItem : IClassFixture + { + /// + /// Tests the id property of the tree item control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("id", @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("03C6031F-04A9-451F-B817-EBD6D32F8B0C", @"
                                                                                                                                                                                                                                                                      • ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItem(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the active property of the tree item control. + /// + [Theory] + [InlineData(TypeActive.None, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeActive.Active, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeActive.Disabled, @"
                                                                                                                                                                                                                                                                      • ")] + public void Active(TypeActive active, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItem() + { + Active = active + }; + + // test execution + var html = control.Render(context, visualTree); + + Assert.Equal(expected, html.Trim()); + } + + /// + /// Tests the layout property of the tree item control. + /// + [Theory] + [InlineData(TypeLayoutTreeItem.Default, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeLayoutTreeItem.Simple, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeLayoutTreeItem.Group, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeLayoutTreeItem.Horizontal, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeLayoutTreeItem.Flat, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeLayoutTreeItem.Flush, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeLayoutTreeItem.TreeView, @"
                                                                                                                                                                                                                                                                      • *
                                                                                                                                                                                                                                                                      • ")] + public void Layout(TypeLayoutTreeItem layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItem() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the expand property of the tree item control. + /// + [Theory] + [InlineData(TypeExpandTree.None, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeExpandTree.Visible, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeExpandTree.Collapse, @"
                                                                                                                                                                                                                                                                      • ")] + public void Expand(TypeExpandTree expand, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItem() + { + Expand = expand + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the tree item control. + /// + [Fact] + public void AddContent() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItem() + { + }; + + // test execution + control.Add(new ControlText() { Text = "abc" }); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • abc
                                                                                                                                                                                                                                                                      • ", html); + } + + /// + /// Tests the add function of the tree item control. + /// + [Fact] + public void AddChild() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItem() + { + }; + + // test execution + control.Add(new ControlTreeItem()); + + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTreeItemLink.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTreeItemLink.cs new file mode 100644 index 00000000..e7059b12 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlTreeItemLink.cs @@ -0,0 +1,222 @@ +ο»Ώusing WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebUri; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebIcon; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the tree item link control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlTreeItemLink + { + /// + /// Tests the id property of the tree item link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("id", @"
                                                                                                                                                                                                                                                                      • ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the text property of the tree item link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("abc", @"
                                                                                                                                                                                                                                                                      • abc
                                                                                                                                                                                                                                                                      • ")] + [InlineData("webexpress.webui:plugin.name", @"
                                                                                                                                                                                                                                                                      • webexpress.webui:plugin.name
                                                                                                                                                                                                                                                                      • ")] + public void Text(string text, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink() + { + Text = text, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the uri property of the tree item link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("/a", @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("/a/b", @"
                                                                                                                                                                                                                                                                      • ")] + public void Uri(string uri, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink() + { + Uri = uri != null ? new UriEndpoint(uri) : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the title property of the tree item link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("a", @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("b", @"
                                                                                                                                                                                                                                                                      • ")] + public void Title(string title, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink() + { + Title = title, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the target property of the tree item link control. + /// + [Theory] + [InlineData(TypeTarget.None, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeTarget.Blank, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeTarget.Self, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeTarget.Parent, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(TypeTarget.Framename, @"
                                                                                                                                                                                                                                                                      • ")] + public void Target(TypeTarget target, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink() + { + Target = target, + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the tooltip property of the tree item link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("a", @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("b", @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData("a
                                                                                                                                                                                                                                                                        b", @"
                                                                                                                                                                                                                                                                      • ")] + public void Tooltip(string tooltip, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink() + { + Tooltip = tooltip + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the icon property of the tree item link control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                      • ")] + [InlineData(typeof(IconStar), @"
                                                                                                                                                                                                                                                                      • ")] + public void Icon(Type icon, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlTreeItemLink() + { + Icon = icon != null ? Activator.CreateInstance(icon) as IIcon : null + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the tree item link control. + /// + [Fact] + public void Add() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control1 = new ControlTreeItemLink(null, new ControlIcon() { Icon = new IconStar() }); + var control2 = new ControlTreeItemLink(null, [new ControlIcon() { Icon = new IconStar() }]); + var control3 = new ControlTreeItemLink(null, new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + var control4 = new ControlTreeItemLink(null); + var control5 = new ControlTreeItemLink(null); + var control6 = new ControlTreeItemLink(null); + + // test execution + control4.Add(new ControlIcon() { Icon = new IconStar() }); + control5.Add([new ControlIcon() { Icon = new IconStar() }]); + control6.Add(new List([new ControlIcon() { Icon = new IconStar() }]).ToArray()); + + var html1 = control1.Render(context, visualTree); + var html2 = control2.Render(context, visualTree); + var html3 = control3.Render(context, visualTree); + var html4 = control4.Render(context, visualTree); + var html5 = control5.Render(context, visualTree); + var html6 = control6.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html1); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html2); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html3); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html4); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html5); + AssertExtensions.EqualWithPlaceholders(@"
                                                                                                                                                                                                                                                                      • ", html6); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebControl/UnitTestControlVirtualList.cs b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlVirtualList.cs new file mode 100644 index 00000000..fd63af0c --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebControl/UnitTestControlVirtualList.cs @@ -0,0 +1,126 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebControl; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebControl +{ + /// + /// Tests the virtual list control. + /// + [Collection("NonParallelTests")] + public class UnitTestControlVirtualList + { + /// + /// Tests the id property of the virtual list control. + /// + [Theory] + [InlineData(null, @"
                                                                                                                                                                                                                                                                          ")] + [InlineData("id", @"
                                                                                                                                                                                                                                                                            ")] + [InlineData("03C6031F-04A9-451F-B817-EBD6D32F8B0C", @"
                                                                                                                                                                                                                                                                              ")] + public void Id(string id, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlVirtualList(id) + { + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the background color property of the virtual list control. + /// + [Theory] + [InlineData(TypeColorBackground.Default, @"
                                                                                                                                                                                                                                                                                ")] + [InlineData(TypeColorBackground.Primary, @"
                                                                                                                                                                                                                                                                                  ")] + [InlineData(TypeColorBackground.Secondary, @"
                                                                                                                                                                                                                                                                                    ")] + [InlineData(TypeColorBackground.Warning, @"
                                                                                                                                                                                                                                                                                      ")] + [InlineData(TypeColorBackground.Danger, @"
                                                                                                                                                                                                                                                                                        ")] + [InlineData(TypeColorBackground.Dark, @"
                                                                                                                                                                                                                                                                                          ")] + [InlineData(TypeColorBackground.Light, @"
                                                                                                                                                                                                                                                                                            ")] + [InlineData(TypeColorBackground.Transparent, @"
                                                                                                                                                                                                                                                                                              ")] + public void BackgroundColor(TypeColorBackground backgroundColor, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlVirtualList() + { + BackgroundColor = new PropertyColorBackground(backgroundColor) + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the layout property of the virtual list control. + /// + [Theory] + [InlineData(TypeLayoutList.Default, @"
                                                                                                                                                                                                                                                                                                ")] + [InlineData(TypeLayoutList.Simple, @"
                                                                                                                                                                                                                                                                                                  ")] + [InlineData(TypeLayoutList.Group, @"
                                                                                                                                                                                                                                                                                                    ")] + [InlineData(TypeLayoutList.Horizontal, @"
                                                                                                                                                                                                                                                                                                      ")] + [InlineData(TypeLayoutList.Flush, @"
                                                                                                                                                                                                                                                                                                        ")] + public void Layout(TypeLayoutList layout, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlVirtualList() + { + Layout = layout + }; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Tests the add function of the virtual list control. + /// + [Theory] + [MemberData(nameof(GetControlListItemsData))] + public void Add(IEnumerable items, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CrerateRenderContextMock(); + var visualTree = new VisualTreeControl(componentHub, context.PageContext); + var control = new ControlVirtualList(null); + + control.RetrieveVirtualItem += (s, e) => e.Items = items; + + // test execution + var html = control.Render(context, visualTree); + + AssertExtensions.EqualWithPlaceholders(expected, html); + } + + /// + /// Provides test data for the Add method of the UnitTestControlList class. + /// + /// An enumerable collection of object arrays, each containing test data. + public static TheoryData, string> GetControlListItemsData() + { + return new TheoryData, string> + { + { new List { new(null, new ControlText() { Text = "Item 1" }) }, @"
                                                                                                                                                                                                                                                                                                        • Item 1
                                                                                                                                                                                                                                                                                                        " }, + { new List { new("id") }, @"
                                                                                                                                                                                                                                                                                                        " }, + { new List { }, "
                                                                                                                                                                                                                                                                                                          " } + }; + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebExpress.WebUI.Test.csproj b/src/WebExpress.WebUI.Test/WebExpress.WebUI.Test.csproj index 05a0080c..ef6a2db7 100644 --- a/src/WebExpress.WebUI.Test/WebExpress.WebUI.Test.csproj +++ b/src/WebExpress.WebUI.Test/WebExpress.WebUI.Test.csproj @@ -1,9 +1,9 @@ - net8.0 + net9.0 enable - enable + disable false true @@ -11,10 +11,14 @@ - - - - + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -24,4 +28,8 @@ + + + + diff --git a/src/WebExpress.WebUI.Test/WebFragment/UnitTestFragmentManager.cs b/src/WebExpress.WebUI.Test/WebFragment/UnitTestFragmentManager.cs new file mode 100644 index 00000000..a54f1d1d --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebFragment/UnitTestFragmentManager.cs @@ -0,0 +1,74 @@ +ο»Ώusing WebExpress.WebCore.WebScope; +using WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebPage; + +namespace WebExpress.WebUI.Test.WebFragment +{ + /// + /// Test the fragment manager. + /// + [Collection("NonParallelTests")] + public class UnitTestFragmentManager + { + /// + /// Test the id property of the fragment manager. + /// + [Theory] + [InlineData(typeof(TestApplication), typeof(TestFragmentControlText), "webexpress.webui.test.testfragmentcontroltext")] + [InlineData(typeof(TestApplication), typeof(TestFragmentControlList), "webexpress.webui.test.testfragmentcontrollist")] + [InlineData(typeof(TestApplication), typeof(TestFragmentControlLink), "webexpress.webui.test.testfragmentcontrollink")] + public void Id(Type applicationType, Type fragmentType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var fragment = componentHub.FragmentManager.GetFragments(application, fragmentType); + + if (id == null) + { + Assert.Empty(fragment); + return; + } + + Assert.Contains(id, fragment.Select(x => x.FragmentId?.ToString())); + } + + /// + /// Test the render function of the fragment manager. + /// + [Theory] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlText), typeof(IScope), @"

                                                                                                                                                                                                                                                                                                          TestFragmentControlText

                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlList), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlLink), typeof(IScope), @"TestFragmentControlLink")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlButtonLink), typeof(IScope), @"TestFragmentControlButtonLink")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlImage), typeof(IScope), @"")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlDropdownItemLink), typeof(IScope), @"TestFragmentControlDropdownItemLink")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlNavigationItemLink), typeof(IScope), @"TestFragmentControlNavigationItemLink")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlPanel), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          TestFragmentControlPanel
                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlPanelFlexbox), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlPanelTool), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                                                                                          TestFragmentControlPanelTool
                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlSplitButtonItemLink), typeof(IScope), @"TestFragmentControlSplitButtonItemLink")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlTree), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlForm), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          *
                                                                                                                                                                                                                                                                                                          ")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlModalForm), typeof(IScope), @"
                                                                                                                                                                                                                                                                                                          *Close
                                                                                                                                                                                                                                                                                                          *")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentControlToolbarItemButton), typeof(IScope), @"TestFragmentControlToolbarItemButton")] + [InlineData(typeof(TestApplication), typeof(TestSectionFragmentHidden), typeof(IScope), null)] + public void Render(Type applicationType, Type sectionType, Type scopeType, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + var renderContext = UnitTestControlFixture.CrerateRenderContextMock(application, [scopeType]); + var visualTree = new VisualTreeControl(componentHub, renderContext.PageContext); + + // test execution + var html = componentHub.FragmentManager.Render(renderContext, visualTree, sectionType); + + Assert.NotNull(html); + Assert.NotEmpty(html); + AssertExtensions.EqualWithPlaceholders(expected, html.FirstOrDefault()?.ToString()); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebNotification/UnitTestNotificationManager.cs b/src/WebExpress.WebUI.Test/WebNotification/UnitTestNotificationManager.cs new file mode 100644 index 00000000..a4a14267 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebNotification/UnitTestNotificationManager.cs @@ -0,0 +1,30 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; +using WebExpress.WebUI.WebNotification; + +namespace WebExpress.WebUI.Test.WebNotification +{ + /// + /// Test the fragment manager. + /// + [Collection("NonParallelTests")] + public class UnitTestNotificationManager + { + /// + /// Test the add notification function of the notification manager. + /// + [Theory] + [InlineData(typeof(TestApplication), "message", 10, "header", "/icon.png", TypeNotification.Success)] + public void AddNotification(Type applicationType, string message, int durability, string heading = null, string icon = null, TypeNotification type = TypeNotification.Light) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var applicationContext = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + var notificationManager = componentHub.GetComponentManager(); + Assert.NotNull(notificationManager); + + // test execution + var notification = notificationManager.AddNotification(applicationContext, message, durability, heading, icon, type); + Assert.NotNull(notification); + } + } +} diff --git a/src/WebExpress.WebUI.Test/WebPage/UnitTestPageManager.cs b/src/WebExpress.WebUI.Test/WebPage/UnitTestPageManager.cs new file mode 100644 index 00000000..b1382f95 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebPage/UnitTestPageManager.cs @@ -0,0 +1,35 @@ +ο»Ώusing WebExpress.WebUI.Test.Fixture; + +namespace WebExpress.WebUI.Test.WebPage +{ + /// + /// Test the page manager. + /// + [Collection("NonParallelTests")] + public class UnitTestPageManager + { + /// + /// Test the id property of the page manager. + /// + [Theory] + [InlineData(typeof(TestApplication), typeof(TestPage), "webexpress.webui.test.testpage")] + public void Id(Type applicationType, Type pageType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var page = componentHub.PageManager.GetPages(pageType, application); + + if (id == null) + { + Assert.Empty(page); + return; + } + + Assert.Contains(id, page.Select(x => x.EndpointId?.ToString())); + } + + } +} diff --git a/src/WebExpress.WebUI.Test/WebStemap/UnitTestSitemapManager.cs b/src/WebExpress.WebUI.Test/WebStemap/UnitTestSitemapManager.cs new file mode 100644 index 00000000..19ede3d7 --- /dev/null +++ b/src/WebExpress.WebUI.Test/WebStemap/UnitTestSitemapManager.cs @@ -0,0 +1,37 @@ +ο»Ώusing WebExpress.WebCore.WebSitemap; +using WebExpress.WebUI.Test.Fixture; + +namespace WebExpress.WebUI.Test.WebStemap +{ + /// + /// Test the sitemap manager. + /// + [Collection("NonParallelTests")] + public class UnitTestSitemapManager + { + /// + /// Test the SearchResource function of the sitemap. + /// + [Theory] + [InlineData("http://localhost:8080/server/app/test", "webexpress.webui.test.testpage")] + public void SearchResource(string uri, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CreateHttpContextMock(); + var httpServerContext = UnitTestControlFixture.CreateHttpServerContextMock(); + var searchContext = Activator.CreateInstance(); + componentHub.SitemapManager.Refresh(); + typeof(SearchContext).GetProperty("HttpServerContext").SetValue(searchContext, httpServerContext); + typeof(SearchContext).GetProperty("Culture").SetValue(searchContext, httpServerContext.Culture); + typeof(SearchContext).GetProperty("HttpContext").SetValue(searchContext, context); + + // test execution + var searchResult = componentHub.SitemapManager.SearchResource(new Uri(uri), searchContext); + + var response = componentHub.EndpointManager.HandleRequest(UnitTestControlFixture.CrerateRequestMock(), searchResult.EndpointContext); + + Assert.Equal(id, searchResult?.EndpointContext?.EndpointId.ToString()); + } + } +} diff --git a/src/WebExpress.WebUI/Assets/css/fontawesome.min.css b/src/WebExpress.WebUI/Assets/css/fontawesome.min.css index 8e36e25a..4e07e306 100644 --- a/src/WebExpress.WebUI/Assets/css/fontawesome.min.css +++ b/src/WebExpress.WebUI/Assets/css/fontawesome.min.css @@ -1,5 +1,9 @@ /*! - * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. */ -.fa,.fab,.fad,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .fa-flip-both,:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-airbnb:before{content:"\f834"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-artstation:before{content:"\f77a"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atlassian:before{content:"\f77b"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-baby:before{content:"\f77c"}.fa-baby-carriage:before{content:"\f77d"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-bacon:before{content:"\f7e5"}.fa-bacteria:before{content:"\e059"}.fa-bacterium:before{content:"\e05a"}.fa-bahai:before{content:"\f666"}.fa-balance-scale:before{content:"\f24e"}.fa-balance-scale-left:before{content:"\f515"}.fa-balance-scale-right:before{content:"\f516"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-battle-net:before{content:"\f835"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-biking:before{content:"\f84a"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-biohazard:before{content:"\f780"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blog:before{content:"\f781"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-medical:before{content:"\f7e6"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bootstrap:before{content:"\f836"}.fa-border-all:before{content:"\f84c"}.fa-border-none:before{content:"\f850"}.fa-border-style:before{content:"\f853"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-box-tissue:before{content:"\e05b"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-bread-slice:before{content:"\f7ec"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-buffer:before{content:"\f837"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buy-n-large:before{content:"\f8a6"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-day:before{content:"\f783"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-calendar-week:before{content:"\f784"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-candy-cane:before{content:"\f786"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caravan:before{content:"\f8ff"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-carrot:before{content:"\f787"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cash-register:before{content:"\f788"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-centos:before{content:"\f789"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-cheese:before{content:"\f7ef"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-chromecast:before{content:"\f838"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clinic-medical:before{content:"\f7f2"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudflare:before{content:"\e07d"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-medical:before{content:"\f7f5"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-compress-alt:before{content:"\f422"}.fa-compress-arrows-alt:before{content:"\f78c"}.fa-concierge-bell:before{content:"\f562"}.fa-confluence:before{content:"\f78d"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-cotton-bureau:before{content:"\f89e"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-crutch:before{content:"\f7f7"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dailymotion:before{content:"\e052"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-deezer:before{content:"\e077"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-dhl:before{content:"\f790"}.fa-diagnoses:before{content:"\f470"}.fa-diaspora:before{content:"\f791"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-disease:before{content:"\f7fa"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dumpster:before{content:"\f793"}.fa-dumpster-fire:before{content:"\f794"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edge-legacy:before{content:"\e078"}.fa-edit:before{content:"\f044"}.fa-egg:before{content:"\f7fb"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-ethernet:before{content:"\f796"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-evernote:before{content:"\f839"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-alt:before{content:"\f424"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fan:before{content:"\f863"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-faucet:before{content:"\e005"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-fedex:before{content:"\f797"}.fa-fedora:before{content:"\f798"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-figma:before{content:"\f799"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-alt:before{content:"\f7e4"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-firefox-browser:before{content:"\e007"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-gifts:before{content:"\f79c"}.fa-git:before{content:"\f1d3"}.fa-git-alt:before{content:"\f841"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-cheers:before{content:"\f79f"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glass-whiskey:before{content:"\f7a0"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-globe-europe:before{content:"\f7a2"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-pay:before{content:"\e079"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-lines:before{content:"\f7a4"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-guilded:before{content:"\e07e"}.fa-guitar:before{content:"\f7a6"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hamburger:before{content:"\f805"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-holding-water:before{content:"\f4c1"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-middle-finger:before{content:"\f806"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-sparkles:before{content:"\e05d"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-hands-wash:before{content:"\e05e"}.fa-handshake:before{content:"\f2b5"}.fa-handshake-alt-slash:before{content:"\e05f"}.fa-handshake-slash:before{content:"\e060"}.fa-hanukiah:before{content:"\f6e6"}.fa-hard-hat:before{content:"\f807"}.fa-hashtag:before{content:"\f292"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-hat-wizard:before{content:"\f6e8"}.fa-hdd:before{content:"\f0a0"}.fa-head-side-cough:before{content:"\e061"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-head-side-mask:before{content:"\e063"}.fa-head-side-virus:before{content:"\e064"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heart-broken:before{content:"\f7a9"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hive:before{content:"\e07f"}.fa-hockey-puck:before{content:"\f453"}.fa-holly-berry:before{content:"\f7aa"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-horse-head:before{content:"\f7ab"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hospital-user:before{content:"\f80d"}.fa-hot-tub:before{content:"\f593"}.fa-hotdog:before{content:"\f80f"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-house-user:before{content:"\e065"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-ice-cream:before{content:"\f810"}.fa-icicles:before{content:"\f7ad"}.fa-icons:before{content:"\f86d"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-ideal:before{content:"\e013"}.fa-igloo:before{content:"\f7ae"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-innosoft:before{content:"\e080"}.fa-instagram:before{content:"\f16d"}.fa-instagram-square:before{content:"\e055"}.fa-instalod:before{content:"\e081"}.fa-intercom:before{content:"\f7af"}.fa-internet-explorer:before{content:"\f26b"}.fa-invision:before{content:"\f7b0"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itch-io:before{content:"\f83a"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-jira:before{content:"\f7b1"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laptop-house:before{content:"\e066"}.fa-laptop-medical:before{content:"\f812"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lungs:before{content:"\f604"}.fa-lungs-virus:before{content:"\e067"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-mdb:before{content:"\f8ca"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-mendeley:before{content:"\f7b3"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microblog:before{content:"\e01a"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mitten:before{content:"\f7b5"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mixer:before{content:"\e056"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse:before{content:"\f8cc"}.fa-mouse-pointer:before{content:"\f245"}.fa-mug-hot:before{content:"\f7b6"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-octopus-deploy:before{content:"\e082"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-orcid:before{content:"\f8d2"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-pager:before{content:"\f815"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-arrows:before{content:"\e068"}.fa-people-carry:before{content:"\f4ce"}.fa-pepper-hot:before{content:"\f816"}.fa-perbyte:before{content:"\e083"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-alt:before{content:"\f879"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-square-alt:before{content:"\f87b"}.fa-phone-volume:before{content:"\f2a0"}.fa-photo-video:before{content:"\f87c"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-square:before{content:"\e01e"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-pizza-slice:before{content:"\f818"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-plane-slash:before{content:"\e069"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pump-medical:before{content:"\e06a"}.fa-pump-soap:before{content:"\e06b"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-radiation:before{content:"\f7b9"}.fa-radiation-alt:before{content:"\f7ba"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-record-vinyl:before{content:"\f8d9"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redhat:before{content:"\f7bc"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-remove-format:before{content:"\f87d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-restroom:before{content:"\f7bd"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-rust:before{content:"\e07a"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-salesforce:before{content:"\f83b"}.fa-sass:before{content:"\f41e"}.fa-satellite:before{content:"\f7bf"}.fa-satellite-dish:before{content:"\f7c0"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-sd-card:before{content:"\f7c2"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-shield-virus:before{content:"\e06c"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopify:before{content:"\e057"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-sim-card:before{content:"\f7c4"}.fa-simplybuilt:before{content:"\f215"}.fa-sink:before{content:"\e06d"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skating:before{content:"\f7c5"}.fa-sketch:before{content:"\f7c6"}.fa-skiing:before{content:"\f7c9"}.fa-skiing-nordic:before{content:"\f7ca"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sleigh:before{content:"\f7cc"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-sms:before{content:"\f7cd"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowboarding:before{content:"\f7ce"}.fa-snowflake:before{content:"\f2dc"}.fa-snowman:before{content:"\f7d0"}.fa-snowplow:before{content:"\f7d2"}.fa-soap:before{content:"\e06e"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-down-alt:before{content:"\f884"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-amount-up-alt:before{content:"\f885"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-sourcetree:before{content:"\f7d3"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-speaker-deck:before{content:"\f83c"}.fa-spell-check:before{content:"\f891"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stackpath:before{content:"\f842"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-stopwatch-20:before{content:"\e06f"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-store-alt-slash:before{content:"\e070"}.fa-store-slash:before{content:"\e071"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-suse:before{content:"\f7d6"}.fa-swatchbook:before{content:"\f5c3"}.fa-swift:before{content:"\f8e1"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-symfony:before{content:"\f83d"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-tenge:before{content:"\f7d7"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-tiktok:before{content:"\e07b"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet:before{content:"\f7d8"}.fa-toilet-paper:before{content:"\f71e"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-toolbox:before{content:"\f552"}.fa-tools:before{content:"\f7d9"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-trailer:before{content:"\e041"}.fa-train:before{content:"\f238"}.fa-tram:before{content:"\f7da"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-trash-restore:before{content:"\f829"}.fa-trash-restore-alt:before{content:"\f82a"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-ubuntu:before{content:"\f7df"}.fa-uikit:before{content:"\f403"}.fa-umbraco:before{content:"\f8e8"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-uncharted:before{content:"\e084"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-unity:before{content:"\e049"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-unsplash:before{content:"\e07c"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-ups:before{content:"\f7e0"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-nurse:before{content:"\f82f"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-users-slash:before{content:"\e073"}.fa-usps:before{content:"\f7e1"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-vest:before{content:"\e085"}.fa-vest-patches:before{content:"\e086"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-virus:before{content:"\e074"}.fa-virus-slash:before{content:"\e075"}.fa-viruses:before{content:"\e076"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-voicemail:before{content:"\f897"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-watchman-monitoring:before{content:"\e087"}.fa-water:before{content:"\f773"}.fa-wave-square:before{content:"\f83e"}.fa-waze:before{content:"\f83f"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wodu:before{content:"\e088"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yammer:before{content:"\f840"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yarn:before{content:"\f7e3"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto} \ No newline at end of file +.fa{font-family:var(--fa-style-family,"Font Awesome 6 Free");font-weight:var(--fa-style,900)}.fa,.fa-brands,.fa-regular,.fa-solid,.fab,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:var(--fa-display,inline-block);font-style:normal;font-variant:normal;line-height:1;text-rendering:auto}.fa-brands:before,.fa-regular:before,.fa-solid:before,.fa:before,.fab:before,.far:before,.fas:before{content:var(--fa)}.fa-classic,.fa-regular,.fa-solid,.far,.fas{font-family:"Font Awesome 6 Free"}.fa-brands,.fab{font-family:"Font Awesome 6 Brands"}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-2xs{font-size:.625em;line-height:.1em;vertical-align:.225em}.fa-xs{font-size:.75em;line-height:.08333em;vertical-align:.125em}.fa-sm{font-size:.875em;line-height:.07143em;vertical-align:.05357em}.fa-lg{font-size:1.25em;line-height:.05em;vertical-align:-.075em}.fa-xl{font-size:1.5em;line-height:.04167em;vertical-align:-.125em}.fa-2xl{font-size:2em;line-height:.03125em;vertical-align:-.1875em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:var(--fa-li-margin,2.5em);padding-left:0}.fa-ul>li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{animation-name:fa-beat;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{animation-name:fa-bounce;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{animation-name:fa-fade;animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{animation-name:fa-beat-fade;animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{animation-name:fa-flip;animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{animation-name:fa-shake;animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{animation-delay:var(--fa-animation-delay,0s);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{animation-name:fa-spin;animation-duration:var(--fa-animation-duration,2s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{animation-name:fa-spin;animation-direction:var(--fa-animation-direction,normal);animation-duration:var(--fa-animation-duration,1s);animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{animation-delay:-1ms;animation-duration:1ms;animation-iteration-count:1;transition-delay:0s;transition-duration:0s}}@keyframes fa-beat{0%,90%{transform:scale(1)}45%{transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-bounce{0%{transform:scale(1) translateY(0)}10%{transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{transform:scale(1) translateY(0)}to{transform:scale(1) translateY(0)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);transform:scale(1)}50%{opacity:1;transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-flip{50%{transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-shake{0%{transform:rotate(-15deg)}4%{transform:rotate(15deg)}8%,24%{transform:rotate(-18deg)}12%,28%{transform:rotate(18deg)}16%{transform:rotate(-22deg)}20%{transform:rotate(22deg)}32%{transform:rotate(-12deg)}36%{transform:rotate(12deg)}40%,to{transform:rotate(0deg)}}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{transform:rotate(90deg)}.fa-rotate-180{transform:rotate(180deg)}.fa-rotate-270{transform:rotate(270deg)}.fa-flip-horizontal{transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}.fa-rotate-by{transform:rotate(var(--fa-rotate-angle,0))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} + +.fa-0{--fa:"\30"}.fa-1{--fa:"\31"}.fa-2{--fa:"\32"}.fa-3{--fa:"\33"}.fa-4{--fa:"\34"}.fa-5{--fa:"\35"}.fa-6{--fa:"\36"}.fa-7{--fa:"\37"}.fa-8{--fa:"\38"}.fa-9{--fa:"\39"}.fa-fill-drip{--fa:"\f576"}.fa-arrows-to-circle{--fa:"\e4bd"}.fa-chevron-circle-right,.fa-circle-chevron-right{--fa:"\f138"}.fa-at{--fa:"\40"}.fa-trash-alt,.fa-trash-can{--fa:"\f2ed"}.fa-text-height{--fa:"\f034"}.fa-user-times,.fa-user-xmark{--fa:"\f235"}.fa-stethoscope{--fa:"\f0f1"}.fa-comment-alt,.fa-message{--fa:"\f27a"}.fa-info{--fa:"\f129"}.fa-compress-alt,.fa-down-left-and-up-right-to-center{--fa:"\f422"}.fa-explosion{--fa:"\e4e9"}.fa-file-alt,.fa-file-lines,.fa-file-text{--fa:"\f15c"}.fa-wave-square{--fa:"\f83e"}.fa-ring{--fa:"\f70b"}.fa-building-un{--fa:"\e4d9"}.fa-dice-three{--fa:"\f527"}.fa-calendar-alt,.fa-calendar-days{--fa:"\f073"}.fa-anchor-circle-check{--fa:"\e4aa"}.fa-building-circle-arrow-right{--fa:"\e4d1"}.fa-volleyball,.fa-volleyball-ball{--fa:"\f45f"}.fa-arrows-up-to-line{--fa:"\e4c2"}.fa-sort-desc,.fa-sort-down{--fa:"\f0dd"}.fa-circle-minus,.fa-minus-circle{--fa:"\f056"}.fa-door-open{--fa:"\f52b"}.fa-right-from-bracket,.fa-sign-out-alt{--fa:"\f2f5"}.fa-atom{--fa:"\f5d2"}.fa-soap{--fa:"\e06e"}.fa-heart-music-camera-bolt,.fa-icons{--fa:"\f86d"}.fa-microphone-alt-slash,.fa-microphone-lines-slash{--fa:"\f539"}.fa-bridge-circle-check{--fa:"\e4c9"}.fa-pump-medical{--fa:"\e06a"}.fa-fingerprint{--fa:"\f577"}.fa-hand-point-right{--fa:"\f0a4"}.fa-magnifying-glass-location,.fa-search-location{--fa:"\f689"}.fa-forward-step,.fa-step-forward{--fa:"\f051"}.fa-face-smile-beam,.fa-smile-beam{--fa:"\f5b8"}.fa-flag-checkered{--fa:"\f11e"}.fa-football,.fa-football-ball{--fa:"\f44e"}.fa-school-circle-exclamation{--fa:"\e56c"}.fa-crop{--fa:"\f125"}.fa-angle-double-down,.fa-angles-down{--fa:"\f103"}.fa-users-rectangle{--fa:"\e594"}.fa-people-roof{--fa:"\e537"}.fa-people-line{--fa:"\e534"}.fa-beer,.fa-beer-mug-empty{--fa:"\f0fc"}.fa-diagram-predecessor{--fa:"\e477"}.fa-arrow-up-long,.fa-long-arrow-up{--fa:"\f176"}.fa-burn,.fa-fire-flame-simple{--fa:"\f46a"}.fa-male,.fa-person{--fa:"\f183"}.fa-laptop{--fa:"\f109"}.fa-file-csv{--fa:"\f6dd"}.fa-menorah{--fa:"\f676"}.fa-truck-plane{--fa:"\e58f"}.fa-record-vinyl{--fa:"\f8d9"}.fa-face-grin-stars,.fa-grin-stars{--fa:"\f587"}.fa-bong{--fa:"\f55c"}.fa-pastafarianism,.fa-spaghetti-monster-flying{--fa:"\f67b"}.fa-arrow-down-up-across-line{--fa:"\e4af"}.fa-spoon,.fa-utensil-spoon{--fa:"\f2e5"}.fa-jar-wheat{--fa:"\e517"}.fa-envelopes-bulk,.fa-mail-bulk{--fa:"\f674"}.fa-file-circle-exclamation{--fa:"\e4eb"}.fa-circle-h,.fa-hospital-symbol{--fa:"\f47e"}.fa-pager{--fa:"\f815"}.fa-address-book,.fa-contact-book{--fa:"\f2b9"}.fa-strikethrough{--fa:"\f0cc"}.fa-k{--fa:"\4b"}.fa-landmark-flag{--fa:"\e51c"}.fa-pencil,.fa-pencil-alt{--fa:"\f303"}.fa-backward{--fa:"\f04a"}.fa-caret-right{--fa:"\f0da"}.fa-comments{--fa:"\f086"}.fa-file-clipboard,.fa-paste{--fa:"\f0ea"}.fa-code-pull-request{--fa:"\e13c"}.fa-clipboard-list{--fa:"\f46d"}.fa-truck-loading,.fa-truck-ramp-box{--fa:"\f4de"}.fa-user-check{--fa:"\f4fc"}.fa-vial-virus{--fa:"\e597"}.fa-sheet-plastic{--fa:"\e571"}.fa-blog{--fa:"\f781"}.fa-user-ninja{--fa:"\f504"}.fa-person-arrow-up-from-line{--fa:"\e539"}.fa-scroll-torah,.fa-torah{--fa:"\f6a0"}.fa-broom-ball,.fa-quidditch,.fa-quidditch-broom-ball{--fa:"\f458"}.fa-toggle-off{--fa:"\f204"}.fa-archive,.fa-box-archive{--fa:"\f187"}.fa-person-drowning{--fa:"\e545"}.fa-arrow-down-9-1,.fa-sort-numeric-desc,.fa-sort-numeric-down-alt{--fa:"\f886"}.fa-face-grin-tongue-squint,.fa-grin-tongue-squint{--fa:"\f58a"}.fa-spray-can{--fa:"\f5bd"}.fa-truck-monster{--fa:"\f63b"}.fa-w{--fa:"\57"}.fa-earth-africa,.fa-globe-africa{--fa:"\f57c"}.fa-rainbow{--fa:"\f75b"}.fa-circle-notch{--fa:"\f1ce"}.fa-tablet-alt,.fa-tablet-screen-button{--fa:"\f3fa"}.fa-paw{--fa:"\f1b0"}.fa-cloud{--fa:"\f0c2"}.fa-trowel-bricks{--fa:"\e58a"}.fa-face-flushed,.fa-flushed{--fa:"\f579"}.fa-hospital-user{--fa:"\f80d"}.fa-tent-arrow-left-right{--fa:"\e57f"}.fa-gavel,.fa-legal{--fa:"\f0e3"}.fa-binoculars{--fa:"\f1e5"}.fa-microphone-slash{--fa:"\f131"}.fa-box-tissue{--fa:"\e05b"}.fa-motorcycle{--fa:"\f21c"}.fa-bell-concierge,.fa-concierge-bell{--fa:"\f562"}.fa-pen-ruler,.fa-pencil-ruler{--fa:"\f5ae"}.fa-people-arrows,.fa-people-arrows-left-right{--fa:"\e068"}.fa-mars-and-venus-burst{--fa:"\e523"}.fa-caret-square-right,.fa-square-caret-right{--fa:"\f152"}.fa-cut,.fa-scissors{--fa:"\f0c4"}.fa-sun-plant-wilt{--fa:"\e57a"}.fa-toilets-portable{--fa:"\e584"}.fa-hockey-puck{--fa:"\f453"}.fa-table{--fa:"\f0ce"}.fa-magnifying-glass-arrow-right{--fa:"\e521"}.fa-digital-tachograph,.fa-tachograph-digital{--fa:"\f566"}.fa-users-slash{--fa:"\e073"}.fa-clover{--fa:"\e139"}.fa-mail-reply,.fa-reply{--fa:"\f3e5"}.fa-star-and-crescent{--fa:"\f699"}.fa-house-fire{--fa:"\e50c"}.fa-minus-square,.fa-square-minus{--fa:"\f146"}.fa-helicopter{--fa:"\f533"}.fa-compass{--fa:"\f14e"}.fa-caret-square-down,.fa-square-caret-down{--fa:"\f150"}.fa-file-circle-question{--fa:"\e4ef"}.fa-laptop-code{--fa:"\f5fc"}.fa-swatchbook{--fa:"\f5c3"}.fa-prescription-bottle{--fa:"\f485"}.fa-bars,.fa-navicon{--fa:"\f0c9"}.fa-people-group{--fa:"\e533"}.fa-hourglass-3,.fa-hourglass-end{--fa:"\f253"}.fa-heart-broken,.fa-heart-crack{--fa:"\f7a9"}.fa-external-link-square-alt,.fa-square-up-right{--fa:"\f360"}.fa-face-kiss-beam,.fa-kiss-beam{--fa:"\f597"}.fa-film{--fa:"\f008"}.fa-ruler-horizontal{--fa:"\f547"}.fa-people-robbery{--fa:"\e536"}.fa-lightbulb{--fa:"\f0eb"}.fa-caret-left{--fa:"\f0d9"}.fa-circle-exclamation,.fa-exclamation-circle{--fa:"\f06a"}.fa-school-circle-xmark{--fa:"\e56d"}.fa-arrow-right-from-bracket,.fa-sign-out{--fa:"\f08b"}.fa-chevron-circle-down,.fa-circle-chevron-down{--fa:"\f13a"}.fa-unlock-alt,.fa-unlock-keyhole{--fa:"\f13e"}.fa-cloud-showers-heavy{--fa:"\f740"}.fa-headphones-alt,.fa-headphones-simple{--fa:"\f58f"}.fa-sitemap{--fa:"\f0e8"}.fa-circle-dollar-to-slot,.fa-donate{--fa:"\f4b9"}.fa-memory{--fa:"\f538"}.fa-road-spikes{--fa:"\e568"}.fa-fire-burner{--fa:"\e4f1"}.fa-flag{--fa:"\f024"}.fa-hanukiah{--fa:"\f6e6"}.fa-feather{--fa:"\f52d"}.fa-volume-down,.fa-volume-low{--fa:"\f027"}.fa-comment-slash{--fa:"\f4b3"}.fa-cloud-sun-rain{--fa:"\f743"}.fa-compress{--fa:"\f066"}.fa-wheat-alt,.fa-wheat-awn{--fa:"\e2cd"}.fa-ankh{--fa:"\f644"}.fa-hands-holding-child{--fa:"\e4fa"}.fa-asterisk{--fa:"\2a"}.fa-check-square,.fa-square-check{--fa:"\f14a"}.fa-peseta-sign{--fa:"\e221"}.fa-header,.fa-heading{--fa:"\f1dc"}.fa-ghost{--fa:"\f6e2"}.fa-list,.fa-list-squares{--fa:"\f03a"}.fa-phone-square-alt,.fa-square-phone-flip{--fa:"\f87b"}.fa-cart-plus{--fa:"\f217"}.fa-gamepad{--fa:"\f11b"}.fa-circle-dot,.fa-dot-circle{--fa:"\f192"}.fa-dizzy,.fa-face-dizzy{--fa:"\f567"}.fa-egg{--fa:"\f7fb"}.fa-house-medical-circle-xmark{--fa:"\e513"}.fa-campground{--fa:"\f6bb"}.fa-folder-plus{--fa:"\f65e"}.fa-futbol,.fa-futbol-ball,.fa-soccer-ball{--fa:"\f1e3"}.fa-paint-brush,.fa-paintbrush{--fa:"\f1fc"}.fa-lock{--fa:"\f023"}.fa-gas-pump{--fa:"\f52f"}.fa-hot-tub,.fa-hot-tub-person{--fa:"\f593"}.fa-map-location,.fa-map-marked{--fa:"\f59f"}.fa-house-flood-water{--fa:"\e50e"}.fa-tree{--fa:"\f1bb"}.fa-bridge-lock{--fa:"\e4cc"}.fa-sack-dollar{--fa:"\f81d"}.fa-edit,.fa-pen-to-square{--fa:"\f044"}.fa-car-side{--fa:"\f5e4"}.fa-share-alt,.fa-share-nodes{--fa:"\f1e0"}.fa-heart-circle-minus{--fa:"\e4ff"}.fa-hourglass-2,.fa-hourglass-half{--fa:"\f252"}.fa-microscope{--fa:"\f610"}.fa-sink{--fa:"\e06d"}.fa-bag-shopping,.fa-shopping-bag{--fa:"\f290"}.fa-arrow-down-z-a,.fa-sort-alpha-desc,.fa-sort-alpha-down-alt{--fa:"\f881"}.fa-mitten{--fa:"\f7b5"}.fa-person-rays{--fa:"\e54d"}.fa-users{--fa:"\f0c0"}.fa-eye-slash{--fa:"\f070"}.fa-flask-vial{--fa:"\e4f3"}.fa-hand,.fa-hand-paper{--fa:"\f256"}.fa-om{--fa:"\f679"}.fa-worm{--fa:"\e599"}.fa-house-circle-xmark{--fa:"\e50b"}.fa-plug{--fa:"\f1e6"}.fa-chevron-up{--fa:"\f077"}.fa-hand-spock{--fa:"\f259"}.fa-stopwatch{--fa:"\f2f2"}.fa-face-kiss,.fa-kiss{--fa:"\f596"}.fa-bridge-circle-xmark{--fa:"\e4cb"}.fa-face-grin-tongue,.fa-grin-tongue{--fa:"\f589"}.fa-chess-bishop{--fa:"\f43a"}.fa-face-grin-wink,.fa-grin-wink{--fa:"\f58c"}.fa-deaf,.fa-deafness,.fa-ear-deaf,.fa-hard-of-hearing{--fa:"\f2a4"}.fa-road-circle-check{--fa:"\e564"}.fa-dice-five{--fa:"\f523"}.fa-rss-square,.fa-square-rss{--fa:"\f143"}.fa-land-mine-on{--fa:"\e51b"}.fa-i-cursor{--fa:"\f246"}.fa-stamp{--fa:"\f5bf"}.fa-stairs{--fa:"\e289"}.fa-i{--fa:"\49"}.fa-hryvnia,.fa-hryvnia-sign{--fa:"\f6f2"}.fa-pills{--fa:"\f484"}.fa-face-grin-wide,.fa-grin-alt{--fa:"\f581"}.fa-tooth{--fa:"\f5c9"}.fa-v{--fa:"\56"}.fa-bangladeshi-taka-sign{--fa:"\e2e6"}.fa-bicycle{--fa:"\f206"}.fa-rod-asclepius,.fa-rod-snake,.fa-staff-aesculapius,.fa-staff-snake{--fa:"\e579"}.fa-head-side-cough-slash{--fa:"\e062"}.fa-ambulance,.fa-truck-medical{--fa:"\f0f9"}.fa-wheat-awn-circle-exclamation{--fa:"\e598"}.fa-snowman{--fa:"\f7d0"}.fa-mortar-pestle{--fa:"\f5a7"}.fa-road-barrier{--fa:"\e562"}.fa-school{--fa:"\f549"}.fa-igloo{--fa:"\f7ae"}.fa-joint{--fa:"\f595"}.fa-angle-right{--fa:"\f105"}.fa-horse{--fa:"\f6f0"}.fa-q{--fa:"\51"}.fa-g{--fa:"\47"}.fa-notes-medical{--fa:"\f481"}.fa-temperature-2,.fa-temperature-half,.fa-thermometer-2,.fa-thermometer-half{--fa:"\f2c9"}.fa-dong-sign{--fa:"\e169"}.fa-capsules{--fa:"\f46b"}.fa-poo-bolt,.fa-poo-storm{--fa:"\f75a"}.fa-face-frown-open,.fa-frown-open{--fa:"\f57a"}.fa-hand-point-up{--fa:"\f0a6"}.fa-money-bill{--fa:"\f0d6"}.fa-bookmark{--fa:"\f02e"}.fa-align-justify{--fa:"\f039"}.fa-umbrella-beach{--fa:"\f5ca"}.fa-helmet-un{--fa:"\e503"}.fa-bullseye{--fa:"\f140"}.fa-bacon{--fa:"\f7e5"}.fa-hand-point-down{--fa:"\f0a7"}.fa-arrow-up-from-bracket{--fa:"\e09a"}.fa-folder,.fa-folder-blank{--fa:"\f07b"}.fa-file-medical-alt,.fa-file-waveform{--fa:"\f478"}.fa-radiation{--fa:"\f7b9"}.fa-chart-simple{--fa:"\e473"}.fa-mars-stroke{--fa:"\f229"}.fa-vial{--fa:"\f492"}.fa-dashboard,.fa-gauge,.fa-gauge-med,.fa-tachometer-alt-average{--fa:"\f624"}.fa-magic-wand-sparkles,.fa-wand-magic-sparkles{--fa:"\e2ca"}.fa-e{--fa:"\45"}.fa-pen-alt,.fa-pen-clip{--fa:"\f305"}.fa-bridge-circle-exclamation{--fa:"\e4ca"}.fa-user{--fa:"\f007"}.fa-school-circle-check{--fa:"\e56b"}.fa-dumpster{--fa:"\f793"}.fa-shuttle-van,.fa-van-shuttle{--fa:"\f5b6"}.fa-building-user{--fa:"\e4da"}.fa-caret-square-left,.fa-square-caret-left{--fa:"\f191"}.fa-highlighter{--fa:"\f591"}.fa-key{--fa:"\f084"}.fa-bullhorn{--fa:"\f0a1"}.fa-globe{--fa:"\f0ac"}.fa-synagogue{--fa:"\f69b"}.fa-person-half-dress{--fa:"\e548"}.fa-road-bridge{--fa:"\e563"}.fa-location-arrow{--fa:"\f124"}.fa-c{--fa:"\43"}.fa-tablet-button{--fa:"\f10a"}.fa-building-lock{--fa:"\e4d6"}.fa-pizza-slice{--fa:"\f818"}.fa-money-bill-wave{--fa:"\f53a"}.fa-area-chart,.fa-chart-area{--fa:"\f1fe"}.fa-house-flag{--fa:"\e50d"}.fa-person-circle-minus{--fa:"\e540"}.fa-ban,.fa-cancel{--fa:"\f05e"}.fa-camera-rotate{--fa:"\e0d8"}.fa-air-freshener,.fa-spray-can-sparkles{--fa:"\f5d0"}.fa-star{--fa:"\f005"}.fa-repeat{--fa:"\f363"}.fa-cross{--fa:"\f654"}.fa-box{--fa:"\f466"}.fa-venus-mars{--fa:"\f228"}.fa-arrow-pointer,.fa-mouse-pointer{--fa:"\f245"}.fa-expand-arrows-alt,.fa-maximize{--fa:"\f31e"}.fa-charging-station{--fa:"\f5e7"}.fa-shapes,.fa-triangle-circle-square{--fa:"\f61f"}.fa-random,.fa-shuffle{--fa:"\f074"}.fa-person-running,.fa-running{--fa:"\f70c"}.fa-mobile-retro{--fa:"\e527"}.fa-grip-lines-vertical{--fa:"\f7a5"}.fa-spider{--fa:"\f717"}.fa-hands-bound{--fa:"\e4f9"}.fa-file-invoice-dollar{--fa:"\f571"}.fa-plane-circle-exclamation{--fa:"\e556"}.fa-x-ray{--fa:"\f497"}.fa-spell-check{--fa:"\f891"}.fa-slash{--fa:"\f715"}.fa-computer-mouse,.fa-mouse{--fa:"\f8cc"}.fa-arrow-right-to-bracket,.fa-sign-in{--fa:"\f090"}.fa-shop-slash,.fa-store-alt-slash{--fa:"\e070"}.fa-server{--fa:"\f233"}.fa-virus-covid-slash{--fa:"\e4a9"}.fa-shop-lock{--fa:"\e4a5"}.fa-hourglass-1,.fa-hourglass-start{--fa:"\f251"}.fa-blender-phone{--fa:"\f6b6"}.fa-building-wheat{--fa:"\e4db"}.fa-person-breastfeeding{--fa:"\e53a"}.fa-right-to-bracket,.fa-sign-in-alt{--fa:"\f2f6"}.fa-venus{--fa:"\f221"}.fa-passport{--fa:"\f5ab"}.fa-thumb-tack-slash,.fa-thumbtack-slash{--fa:"\e68f"}.fa-heart-pulse,.fa-heartbeat{--fa:"\f21e"}.fa-people-carry,.fa-people-carry-box{--fa:"\f4ce"}.fa-temperature-high{--fa:"\f769"}.fa-microchip{--fa:"\f2db"}.fa-crown{--fa:"\f521"}.fa-weight-hanging{--fa:"\f5cd"}.fa-xmarks-lines{--fa:"\e59a"}.fa-file-prescription{--fa:"\f572"}.fa-weight,.fa-weight-scale{--fa:"\f496"}.fa-user-friends,.fa-user-group{--fa:"\f500"}.fa-arrow-up-a-z,.fa-sort-alpha-up{--fa:"\f15e"}.fa-chess-knight{--fa:"\f441"}.fa-face-laugh-squint,.fa-laugh-squint{--fa:"\f59b"}.fa-wheelchair{--fa:"\f193"}.fa-arrow-circle-up,.fa-circle-arrow-up{--fa:"\f0aa"}.fa-toggle-on{--fa:"\f205"}.fa-person-walking,.fa-walking{--fa:"\f554"}.fa-l{--fa:"\4c"}.fa-fire{--fa:"\f06d"}.fa-bed-pulse,.fa-procedures{--fa:"\f487"}.fa-shuttle-space,.fa-space-shuttle{--fa:"\f197"}.fa-face-laugh,.fa-laugh{--fa:"\f599"}.fa-folder-open{--fa:"\f07c"}.fa-heart-circle-plus{--fa:"\e500"}.fa-code-fork{--fa:"\e13b"}.fa-city{--fa:"\f64f"}.fa-microphone-alt,.fa-microphone-lines{--fa:"\f3c9"}.fa-pepper-hot{--fa:"\f816"}.fa-unlock{--fa:"\f09c"}.fa-colon-sign{--fa:"\e140"}.fa-headset{--fa:"\f590"}.fa-store-slash{--fa:"\e071"}.fa-road-circle-xmark{--fa:"\e566"}.fa-user-minus{--fa:"\f503"}.fa-mars-stroke-up,.fa-mars-stroke-v{--fa:"\f22a"}.fa-champagne-glasses,.fa-glass-cheers{--fa:"\f79f"}.fa-clipboard{--fa:"\f328"}.fa-house-circle-exclamation{--fa:"\e50a"}.fa-file-arrow-up,.fa-file-upload{--fa:"\f574"}.fa-wifi,.fa-wifi-3,.fa-wifi-strong{--fa:"\f1eb"}.fa-bath,.fa-bathtub{--fa:"\f2cd"}.fa-underline{--fa:"\f0cd"}.fa-user-edit,.fa-user-pen{--fa:"\f4ff"}.fa-signature{--fa:"\f5b7"}.fa-stroopwafel{--fa:"\f551"}.fa-bold{--fa:"\f032"}.fa-anchor-lock{--fa:"\e4ad"}.fa-building-ngo{--fa:"\e4d7"}.fa-manat-sign{--fa:"\e1d5"}.fa-not-equal{--fa:"\f53e"}.fa-border-style,.fa-border-top-left{--fa:"\f853"}.fa-map-location-dot,.fa-map-marked-alt{--fa:"\f5a0"}.fa-jedi{--fa:"\f669"}.fa-poll,.fa-square-poll-vertical{--fa:"\f681"}.fa-mug-hot{--fa:"\f7b6"}.fa-battery-car,.fa-car-battery{--fa:"\f5df"}.fa-gift{--fa:"\f06b"}.fa-dice-two{--fa:"\f528"}.fa-chess-queen{--fa:"\f445"}.fa-glasses{--fa:"\f530"}.fa-chess-board{--fa:"\f43c"}.fa-building-circle-check{--fa:"\e4d2"}.fa-person-chalkboard{--fa:"\e53d"}.fa-mars-stroke-h,.fa-mars-stroke-right{--fa:"\f22b"}.fa-hand-back-fist,.fa-hand-rock{--fa:"\f255"}.fa-caret-square-up,.fa-square-caret-up{--fa:"\f151"}.fa-cloud-showers-water{--fa:"\e4e4"}.fa-bar-chart,.fa-chart-bar{--fa:"\f080"}.fa-hands-bubbles,.fa-hands-wash{--fa:"\e05e"}.fa-less-than-equal{--fa:"\f537"}.fa-train{--fa:"\f238"}.fa-eye-low-vision,.fa-low-vision{--fa:"\f2a8"}.fa-crow{--fa:"\f520"}.fa-sailboat{--fa:"\e445"}.fa-window-restore{--fa:"\f2d2"}.fa-plus-square,.fa-square-plus{--fa:"\f0fe"}.fa-torii-gate{--fa:"\f6a1"}.fa-frog{--fa:"\f52e"}.fa-bucket{--fa:"\e4cf"}.fa-image{--fa:"\f03e"}.fa-microphone{--fa:"\f130"}.fa-cow{--fa:"\f6c8"}.fa-caret-up{--fa:"\f0d8"}.fa-screwdriver{--fa:"\f54a"}.fa-folder-closed{--fa:"\e185"}.fa-house-tsunami{--fa:"\e515"}.fa-square-nfi{--fa:"\e576"}.fa-arrow-up-from-ground-water{--fa:"\e4b5"}.fa-glass-martini-alt,.fa-martini-glass{--fa:"\f57b"}.fa-square-binary{--fa:"\e69b"}.fa-rotate-back,.fa-rotate-backward,.fa-rotate-left,.fa-undo-alt{--fa:"\f2ea"}.fa-columns,.fa-table-columns{--fa:"\f0db"}.fa-lemon{--fa:"\f094"}.fa-head-side-mask{--fa:"\e063"}.fa-handshake{--fa:"\f2b5"}.fa-gem{--fa:"\f3a5"}.fa-dolly,.fa-dolly-box{--fa:"\f472"}.fa-smoking{--fa:"\f48d"}.fa-compress-arrows-alt,.fa-minimize{--fa:"\f78c"}.fa-monument{--fa:"\f5a6"}.fa-snowplow{--fa:"\f7d2"}.fa-angle-double-right,.fa-angles-right{--fa:"\f101"}.fa-cannabis{--fa:"\f55f"}.fa-circle-play,.fa-play-circle{--fa:"\f144"}.fa-tablets{--fa:"\f490"}.fa-ethernet{--fa:"\f796"}.fa-eur,.fa-euro,.fa-euro-sign{--fa:"\f153"}.fa-chair{--fa:"\f6c0"}.fa-check-circle,.fa-circle-check{--fa:"\f058"}.fa-circle-stop,.fa-stop-circle{--fa:"\f28d"}.fa-compass-drafting,.fa-drafting-compass{--fa:"\f568"}.fa-plate-wheat{--fa:"\e55a"}.fa-icicles{--fa:"\f7ad"}.fa-person-shelter{--fa:"\e54f"}.fa-neuter{--fa:"\f22c"}.fa-id-badge{--fa:"\f2c1"}.fa-marker{--fa:"\f5a1"}.fa-face-laugh-beam,.fa-laugh-beam{--fa:"\f59a"}.fa-helicopter-symbol{--fa:"\e502"}.fa-universal-access{--fa:"\f29a"}.fa-chevron-circle-up,.fa-circle-chevron-up{--fa:"\f139"}.fa-lari-sign{--fa:"\e1c8"}.fa-volcano{--fa:"\f770"}.fa-person-walking-dashed-line-arrow-right{--fa:"\e553"}.fa-gbp,.fa-pound-sign,.fa-sterling-sign{--fa:"\f154"}.fa-viruses{--fa:"\e076"}.fa-square-person-confined{--fa:"\e577"}.fa-user-tie{--fa:"\f508"}.fa-arrow-down-long,.fa-long-arrow-down{--fa:"\f175"}.fa-tent-arrow-down-to-line{--fa:"\e57e"}.fa-certificate{--fa:"\f0a3"}.fa-mail-reply-all,.fa-reply-all{--fa:"\f122"}.fa-suitcase{--fa:"\f0f2"}.fa-person-skating,.fa-skating{--fa:"\f7c5"}.fa-filter-circle-dollar,.fa-funnel-dollar{--fa:"\f662"}.fa-camera-retro{--fa:"\f083"}.fa-arrow-circle-down,.fa-circle-arrow-down{--fa:"\f0ab"}.fa-arrow-right-to-file,.fa-file-import{--fa:"\f56f"}.fa-external-link-square,.fa-square-arrow-up-right{--fa:"\f14c"}.fa-box-open{--fa:"\f49e"}.fa-scroll{--fa:"\f70e"}.fa-spa{--fa:"\f5bb"}.fa-location-pin-lock{--fa:"\e51f"}.fa-pause{--fa:"\f04c"}.fa-hill-avalanche{--fa:"\e507"}.fa-temperature-0,.fa-temperature-empty,.fa-thermometer-0,.fa-thermometer-empty{--fa:"\f2cb"}.fa-bomb{--fa:"\f1e2"}.fa-registered{--fa:"\f25d"}.fa-address-card,.fa-contact-card,.fa-vcard{--fa:"\f2bb"}.fa-balance-scale-right,.fa-scale-unbalanced-flip{--fa:"\f516"}.fa-subscript{--fa:"\f12c"}.fa-diamond-turn-right,.fa-directions{--fa:"\f5eb"}.fa-burst{--fa:"\e4dc"}.fa-house-laptop,.fa-laptop-house{--fa:"\e066"}.fa-face-tired,.fa-tired{--fa:"\f5c8"}.fa-money-bills{--fa:"\e1f3"}.fa-smog{--fa:"\f75f"}.fa-crutch{--fa:"\f7f7"}.fa-cloud-arrow-up,.fa-cloud-upload,.fa-cloud-upload-alt{--fa:"\f0ee"}.fa-palette{--fa:"\f53f"}.fa-arrows-turn-right{--fa:"\e4c0"}.fa-vest{--fa:"\e085"}.fa-ferry{--fa:"\e4ea"}.fa-arrows-down-to-people{--fa:"\e4b9"}.fa-seedling,.fa-sprout{--fa:"\f4d8"}.fa-arrows-alt-h,.fa-left-right{--fa:"\f337"}.fa-boxes-packing{--fa:"\e4c7"}.fa-arrow-circle-left,.fa-circle-arrow-left{--fa:"\f0a8"}.fa-group-arrows-rotate{--fa:"\e4f6"}.fa-bowl-food{--fa:"\e4c6"}.fa-candy-cane{--fa:"\f786"}.fa-arrow-down-wide-short,.fa-sort-amount-asc,.fa-sort-amount-down{--fa:"\f160"}.fa-cloud-bolt,.fa-thunderstorm{--fa:"\f76c"}.fa-remove-format,.fa-text-slash{--fa:"\f87d"}.fa-face-smile-wink,.fa-smile-wink{--fa:"\f4da"}.fa-file-word{--fa:"\f1c2"}.fa-file-powerpoint{--fa:"\f1c4"}.fa-arrows-h,.fa-arrows-left-right{--fa:"\f07e"}.fa-house-lock{--fa:"\e510"}.fa-cloud-arrow-down,.fa-cloud-download,.fa-cloud-download-alt{--fa:"\f0ed"}.fa-children{--fa:"\e4e1"}.fa-blackboard,.fa-chalkboard{--fa:"\f51b"}.fa-user-alt-slash,.fa-user-large-slash{--fa:"\f4fa"}.fa-envelope-open{--fa:"\f2b6"}.fa-handshake-alt-slash,.fa-handshake-simple-slash{--fa:"\e05f"}.fa-mattress-pillow{--fa:"\e525"}.fa-guarani-sign{--fa:"\e19a"}.fa-arrows-rotate,.fa-refresh,.fa-sync{--fa:"\f021"}.fa-fire-extinguisher{--fa:"\f134"}.fa-cruzeiro-sign{--fa:"\e152"}.fa-greater-than-equal{--fa:"\f532"}.fa-shield-alt,.fa-shield-halved{--fa:"\f3ed"}.fa-atlas,.fa-book-atlas{--fa:"\f558"}.fa-virus{--fa:"\e074"}.fa-envelope-circle-check{--fa:"\e4e8"}.fa-layer-group{--fa:"\f5fd"}.fa-arrows-to-dot{--fa:"\e4be"}.fa-archway{--fa:"\f557"}.fa-heart-circle-check{--fa:"\e4fd"}.fa-house-chimney-crack,.fa-house-damage{--fa:"\f6f1"}.fa-file-archive,.fa-file-zipper{--fa:"\f1c6"}.fa-square{--fa:"\f0c8"}.fa-glass-martini,.fa-martini-glass-empty{--fa:"\f000"}.fa-couch{--fa:"\f4b8"}.fa-cedi-sign{--fa:"\e0df"}.fa-italic{--fa:"\f033"}.fa-table-cells-column-lock{--fa:"\e678"}.fa-church{--fa:"\f51d"}.fa-comments-dollar{--fa:"\f653"}.fa-democrat{--fa:"\f747"}.fa-z{--fa:"\5a"}.fa-person-skiing,.fa-skiing{--fa:"\f7c9"}.fa-road-lock{--fa:"\e567"}.fa-a{--fa:"\41"}.fa-temperature-arrow-down,.fa-temperature-down{--fa:"\e03f"}.fa-feather-alt,.fa-feather-pointed{--fa:"\f56b"}.fa-p{--fa:"\50"}.fa-snowflake{--fa:"\f2dc"}.fa-newspaper{--fa:"\f1ea"}.fa-ad,.fa-rectangle-ad{--fa:"\f641"}.fa-arrow-circle-right,.fa-circle-arrow-right{--fa:"\f0a9"}.fa-filter-circle-xmark{--fa:"\e17b"}.fa-locust{--fa:"\e520"}.fa-sort,.fa-unsorted{--fa:"\f0dc"}.fa-list-1-2,.fa-list-numeric,.fa-list-ol{--fa:"\f0cb"}.fa-person-dress-burst{--fa:"\e544"}.fa-money-check-alt,.fa-money-check-dollar{--fa:"\f53d"}.fa-vector-square{--fa:"\f5cb"}.fa-bread-slice{--fa:"\f7ec"}.fa-language{--fa:"\f1ab"}.fa-face-kiss-wink-heart,.fa-kiss-wink-heart{--fa:"\f598"}.fa-filter{--fa:"\f0b0"}.fa-question{--fa:"\3f"}.fa-file-signature{--fa:"\f573"}.fa-arrows-alt,.fa-up-down-left-right{--fa:"\f0b2"}.fa-house-chimney-user{--fa:"\e065"}.fa-hand-holding-heart{--fa:"\f4be"}.fa-puzzle-piece{--fa:"\f12e"}.fa-money-check{--fa:"\f53c"}.fa-star-half-alt,.fa-star-half-stroke{--fa:"\f5c0"}.fa-code{--fa:"\f121"}.fa-glass-whiskey,.fa-whiskey-glass{--fa:"\f7a0"}.fa-building-circle-exclamation{--fa:"\e4d3"}.fa-magnifying-glass-chart{--fa:"\e522"}.fa-arrow-up-right-from-square,.fa-external-link{--fa:"\f08e"}.fa-cubes-stacked{--fa:"\e4e6"}.fa-krw,.fa-won,.fa-won-sign{--fa:"\f159"}.fa-virus-covid{--fa:"\e4a8"}.fa-austral-sign{--fa:"\e0a9"}.fa-f{--fa:"\46"}.fa-leaf{--fa:"\f06c"}.fa-road{--fa:"\f018"}.fa-cab,.fa-taxi{--fa:"\f1ba"}.fa-person-circle-plus{--fa:"\e541"}.fa-chart-pie,.fa-pie-chart{--fa:"\f200"}.fa-bolt-lightning{--fa:"\e0b7"}.fa-sack-xmark{--fa:"\e56a"}.fa-file-excel{--fa:"\f1c3"}.fa-file-contract{--fa:"\f56c"}.fa-fish-fins{--fa:"\e4f2"}.fa-building-flag{--fa:"\e4d5"}.fa-face-grin-beam,.fa-grin-beam{--fa:"\f582"}.fa-object-ungroup{--fa:"\f248"}.fa-poop{--fa:"\f619"}.fa-location-pin,.fa-map-marker{--fa:"\f041"}.fa-kaaba{--fa:"\f66b"}.fa-toilet-paper{--fa:"\f71e"}.fa-hard-hat,.fa-hat-hard,.fa-helmet-safety{--fa:"\f807"}.fa-eject{--fa:"\f052"}.fa-arrow-alt-circle-right,.fa-circle-right{--fa:"\f35a"}.fa-plane-circle-check{--fa:"\e555"}.fa-face-rolling-eyes,.fa-meh-rolling-eyes{--fa:"\f5a5"}.fa-object-group{--fa:"\f247"}.fa-chart-line,.fa-line-chart{--fa:"\f201"}.fa-mask-ventilator{--fa:"\e524"}.fa-arrow-right{--fa:"\f061"}.fa-map-signs,.fa-signs-post{--fa:"\f277"}.fa-cash-register{--fa:"\f788"}.fa-person-circle-question{--fa:"\e542"}.fa-h{--fa:"\48"}.fa-tarp{--fa:"\e57b"}.fa-screwdriver-wrench,.fa-tools{--fa:"\f7d9"}.fa-arrows-to-eye{--fa:"\e4bf"}.fa-plug-circle-bolt{--fa:"\e55b"}.fa-heart{--fa:"\f004"}.fa-mars-and-venus{--fa:"\f224"}.fa-home-user,.fa-house-user{--fa:"\e1b0"}.fa-dumpster-fire{--fa:"\f794"}.fa-house-crack{--fa:"\e3b1"}.fa-cocktail,.fa-martini-glass-citrus{--fa:"\f561"}.fa-face-surprise,.fa-surprise{--fa:"\f5c2"}.fa-bottle-water{--fa:"\e4c5"}.fa-circle-pause,.fa-pause-circle{--fa:"\f28b"}.fa-toilet-paper-slash{--fa:"\e072"}.fa-apple-alt,.fa-apple-whole{--fa:"\f5d1"}.fa-kitchen-set{--fa:"\e51a"}.fa-r{--fa:"\52"}.fa-temperature-1,.fa-temperature-quarter,.fa-thermometer-1,.fa-thermometer-quarter{--fa:"\f2ca"}.fa-cube{--fa:"\f1b2"}.fa-bitcoin-sign{--fa:"\e0b4"}.fa-shield-dog{--fa:"\e573"}.fa-solar-panel{--fa:"\f5ba"}.fa-lock-open{--fa:"\f3c1"}.fa-elevator{--fa:"\e16d"}.fa-money-bill-transfer{--fa:"\e528"}.fa-money-bill-trend-up{--fa:"\e529"}.fa-house-flood-water-circle-arrow-right{--fa:"\e50f"}.fa-poll-h,.fa-square-poll-horizontal{--fa:"\f682"}.fa-circle{--fa:"\f111"}.fa-backward-fast,.fa-fast-backward{--fa:"\f049"}.fa-recycle{--fa:"\f1b8"}.fa-user-astronaut{--fa:"\f4fb"}.fa-plane-slash{--fa:"\e069"}.fa-trademark{--fa:"\f25c"}.fa-basketball,.fa-basketball-ball{--fa:"\f434"}.fa-satellite-dish{--fa:"\f7c0"}.fa-arrow-alt-circle-up,.fa-circle-up{--fa:"\f35b"}.fa-mobile-alt,.fa-mobile-screen-button{--fa:"\f3cd"}.fa-volume-high,.fa-volume-up{--fa:"\f028"}.fa-users-rays{--fa:"\e593"}.fa-wallet{--fa:"\f555"}.fa-clipboard-check{--fa:"\f46c"}.fa-file-audio{--fa:"\f1c7"}.fa-burger,.fa-hamburger{--fa:"\f805"}.fa-wrench{--fa:"\f0ad"}.fa-bugs{--fa:"\e4d0"}.fa-rupee,.fa-rupee-sign{--fa:"\f156"}.fa-file-image{--fa:"\f1c5"}.fa-circle-question,.fa-question-circle{--fa:"\f059"}.fa-plane-departure{--fa:"\f5b0"}.fa-handshake-slash{--fa:"\e060"}.fa-book-bookmark{--fa:"\e0bb"}.fa-code-branch{--fa:"\f126"}.fa-hat-cowboy{--fa:"\f8c0"}.fa-bridge{--fa:"\e4c8"}.fa-phone-alt,.fa-phone-flip{--fa:"\f879"}.fa-truck-front{--fa:"\e2b7"}.fa-cat{--fa:"\f6be"}.fa-anchor-circle-exclamation{--fa:"\e4ab"}.fa-truck-field{--fa:"\e58d"}.fa-route{--fa:"\f4d7"}.fa-clipboard-question{--fa:"\e4e3"}.fa-panorama{--fa:"\e209"}.fa-comment-medical{--fa:"\f7f5"}.fa-teeth-open{--fa:"\f62f"}.fa-file-circle-minus{--fa:"\e4ed"}.fa-tags{--fa:"\f02c"}.fa-wine-glass{--fa:"\f4e3"}.fa-fast-forward,.fa-forward-fast{--fa:"\f050"}.fa-face-meh-blank,.fa-meh-blank{--fa:"\f5a4"}.fa-parking,.fa-square-parking{--fa:"\f540"}.fa-house-signal{--fa:"\e012"}.fa-bars-progress,.fa-tasks-alt{--fa:"\f828"}.fa-faucet-drip{--fa:"\e006"}.fa-cart-flatbed,.fa-dolly-flatbed{--fa:"\f474"}.fa-ban-smoking,.fa-smoking-ban{--fa:"\f54d"}.fa-terminal{--fa:"\f120"}.fa-mobile-button{--fa:"\f10b"}.fa-house-medical-flag{--fa:"\e514"}.fa-basket-shopping,.fa-shopping-basket{--fa:"\f291"}.fa-tape{--fa:"\f4db"}.fa-bus-alt,.fa-bus-simple{--fa:"\f55e"}.fa-eye{--fa:"\f06e"}.fa-face-sad-cry,.fa-sad-cry{--fa:"\f5b3"}.fa-audio-description{--fa:"\f29e"}.fa-person-military-to-person{--fa:"\e54c"}.fa-file-shield{--fa:"\e4f0"}.fa-user-slash{--fa:"\f506"}.fa-pen{--fa:"\f304"}.fa-tower-observation{--fa:"\e586"}.fa-file-code{--fa:"\f1c9"}.fa-signal,.fa-signal-5,.fa-signal-perfect{--fa:"\f012"}.fa-bus{--fa:"\f207"}.fa-heart-circle-xmark{--fa:"\e501"}.fa-home-lg,.fa-house-chimney{--fa:"\e3af"}.fa-window-maximize{--fa:"\f2d0"}.fa-face-frown,.fa-frown{--fa:"\f119"}.fa-prescription{--fa:"\f5b1"}.fa-shop,.fa-store-alt{--fa:"\f54f"}.fa-floppy-disk,.fa-save{--fa:"\f0c7"}.fa-vihara{--fa:"\f6a7"}.fa-balance-scale-left,.fa-scale-unbalanced{--fa:"\f515"}.fa-sort-asc,.fa-sort-up{--fa:"\f0de"}.fa-comment-dots,.fa-commenting{--fa:"\f4ad"}.fa-plant-wilt{--fa:"\e5aa"}.fa-diamond{--fa:"\f219"}.fa-face-grin-squint,.fa-grin-squint{--fa:"\f585"}.fa-hand-holding-dollar,.fa-hand-holding-usd{--fa:"\f4c0"}.fa-chart-diagram{--fa:"\e695"}.fa-bacterium{--fa:"\e05a"}.fa-hand-pointer{--fa:"\f25a"}.fa-drum-steelpan{--fa:"\f56a"}.fa-hand-scissors{--fa:"\f257"}.fa-hands-praying,.fa-praying-hands{--fa:"\f684"}.fa-arrow-right-rotate,.fa-arrow-rotate-forward,.fa-arrow-rotate-right,.fa-redo{--fa:"\f01e"}.fa-biohazard{--fa:"\f780"}.fa-location,.fa-location-crosshairs{--fa:"\f601"}.fa-mars-double{--fa:"\f227"}.fa-child-dress{--fa:"\e59c"}.fa-users-between-lines{--fa:"\e591"}.fa-lungs-virus{--fa:"\e067"}.fa-face-grin-tears,.fa-grin-tears{--fa:"\f588"}.fa-phone{--fa:"\f095"}.fa-calendar-times,.fa-calendar-xmark{--fa:"\f273"}.fa-child-reaching{--fa:"\e59d"}.fa-head-side-virus{--fa:"\e064"}.fa-user-cog,.fa-user-gear{--fa:"\f4fe"}.fa-arrow-up-1-9,.fa-sort-numeric-up{--fa:"\f163"}.fa-door-closed{--fa:"\f52a"}.fa-shield-virus{--fa:"\e06c"}.fa-dice-six{--fa:"\f526"}.fa-mosquito-net{--fa:"\e52c"}.fa-file-fragment{--fa:"\e697"}.fa-bridge-water{--fa:"\e4ce"}.fa-person-booth{--fa:"\f756"}.fa-text-width{--fa:"\f035"}.fa-hat-wizard{--fa:"\f6e8"}.fa-pen-fancy{--fa:"\f5ac"}.fa-digging,.fa-person-digging{--fa:"\f85e"}.fa-trash{--fa:"\f1f8"}.fa-gauge-simple,.fa-gauge-simple-med,.fa-tachometer-average{--fa:"\f629"}.fa-book-medical{--fa:"\f7e6"}.fa-poo{--fa:"\f2fe"}.fa-quote-right,.fa-quote-right-alt{--fa:"\f10e"}.fa-shirt,.fa-t-shirt,.fa-tshirt{--fa:"\f553"}.fa-cubes{--fa:"\f1b3"}.fa-divide{--fa:"\f529"}.fa-tenge,.fa-tenge-sign{--fa:"\f7d7"}.fa-headphones{--fa:"\f025"}.fa-hands-holding{--fa:"\f4c2"}.fa-hands-clapping{--fa:"\e1a8"}.fa-republican{--fa:"\f75e"}.fa-arrow-left{--fa:"\f060"}.fa-person-circle-xmark{--fa:"\e543"}.fa-ruler{--fa:"\f545"}.fa-align-left{--fa:"\f036"}.fa-dice-d6{--fa:"\f6d1"}.fa-restroom{--fa:"\f7bd"}.fa-j{--fa:"\4a"}.fa-users-viewfinder{--fa:"\e595"}.fa-file-video{--fa:"\f1c8"}.fa-external-link-alt,.fa-up-right-from-square{--fa:"\f35d"}.fa-table-cells,.fa-th{--fa:"\f00a"}.fa-file-pdf{--fa:"\f1c1"}.fa-bible,.fa-book-bible{--fa:"\f647"}.fa-o{--fa:"\4f"}.fa-medkit,.fa-suitcase-medical{--fa:"\f0fa"}.fa-user-secret{--fa:"\f21b"}.fa-otter{--fa:"\f700"}.fa-female,.fa-person-dress{--fa:"\f182"}.fa-comment-dollar{--fa:"\f651"}.fa-briefcase-clock,.fa-business-time{--fa:"\f64a"}.fa-table-cells-large,.fa-th-large{--fa:"\f009"}.fa-book-tanakh,.fa-tanakh{--fa:"\f827"}.fa-phone-volume,.fa-volume-control-phone{--fa:"\f2a0"}.fa-hat-cowboy-side{--fa:"\f8c1"}.fa-clipboard-user{--fa:"\f7f3"}.fa-child{--fa:"\f1ae"}.fa-lira-sign{--fa:"\f195"}.fa-satellite{--fa:"\f7bf"}.fa-plane-lock{--fa:"\e558"}.fa-tag{--fa:"\f02b"}.fa-comment{--fa:"\f075"}.fa-birthday-cake,.fa-cake,.fa-cake-candles{--fa:"\f1fd"}.fa-envelope{--fa:"\f0e0"}.fa-angle-double-up,.fa-angles-up{--fa:"\f102"}.fa-paperclip{--fa:"\f0c6"}.fa-arrow-right-to-city{--fa:"\e4b3"}.fa-ribbon{--fa:"\f4d6"}.fa-lungs{--fa:"\f604"}.fa-arrow-up-9-1,.fa-sort-numeric-up-alt{--fa:"\f887"}.fa-litecoin-sign{--fa:"\e1d3"}.fa-border-none{--fa:"\f850"}.fa-circle-nodes{--fa:"\e4e2"}.fa-parachute-box{--fa:"\f4cd"}.fa-indent{--fa:"\f03c"}.fa-truck-field-un{--fa:"\e58e"}.fa-hourglass,.fa-hourglass-empty{--fa:"\f254"}.fa-mountain{--fa:"\f6fc"}.fa-user-doctor,.fa-user-md{--fa:"\f0f0"}.fa-circle-info,.fa-info-circle{--fa:"\f05a"}.fa-cloud-meatball{--fa:"\f73b"}.fa-camera,.fa-camera-alt{--fa:"\f030"}.fa-square-virus{--fa:"\e578"}.fa-meteor{--fa:"\f753"}.fa-car-on{--fa:"\e4dd"}.fa-sleigh{--fa:"\f7cc"}.fa-arrow-down-1-9,.fa-sort-numeric-asc,.fa-sort-numeric-down{--fa:"\f162"}.fa-hand-holding-droplet,.fa-hand-holding-water{--fa:"\f4c1"}.fa-water{--fa:"\f773"}.fa-calendar-check{--fa:"\f274"}.fa-braille{--fa:"\f2a1"}.fa-prescription-bottle-alt,.fa-prescription-bottle-medical{--fa:"\f486"}.fa-landmark{--fa:"\f66f"}.fa-truck{--fa:"\f0d1"}.fa-crosshairs{--fa:"\f05b"}.fa-person-cane{--fa:"\e53c"}.fa-tent{--fa:"\e57d"}.fa-vest-patches{--fa:"\e086"}.fa-check-double{--fa:"\f560"}.fa-arrow-down-a-z,.fa-sort-alpha-asc,.fa-sort-alpha-down{--fa:"\f15d"}.fa-money-bill-wheat{--fa:"\e52a"}.fa-cookie{--fa:"\f563"}.fa-arrow-left-rotate,.fa-arrow-rotate-back,.fa-arrow-rotate-backward,.fa-arrow-rotate-left,.fa-undo{--fa:"\f0e2"}.fa-hard-drive,.fa-hdd{--fa:"\f0a0"}.fa-face-grin-squint-tears,.fa-grin-squint-tears{--fa:"\f586"}.fa-dumbbell{--fa:"\f44b"}.fa-list-alt,.fa-rectangle-list{--fa:"\f022"}.fa-tarp-droplet{--fa:"\e57c"}.fa-house-medical-circle-check{--fa:"\e511"}.fa-person-skiing-nordic,.fa-skiing-nordic{--fa:"\f7ca"}.fa-calendar-plus{--fa:"\f271"}.fa-plane-arrival{--fa:"\f5af"}.fa-arrow-alt-circle-left,.fa-circle-left{--fa:"\f359"}.fa-subway,.fa-train-subway{--fa:"\f239"}.fa-chart-gantt{--fa:"\e0e4"}.fa-indian-rupee,.fa-indian-rupee-sign,.fa-inr{--fa:"\e1bc"}.fa-crop-alt,.fa-crop-simple{--fa:"\f565"}.fa-money-bill-1,.fa-money-bill-alt{--fa:"\f3d1"}.fa-left-long,.fa-long-arrow-alt-left{--fa:"\f30a"}.fa-dna{--fa:"\f471"}.fa-virus-slash{--fa:"\e075"}.fa-minus,.fa-subtract{--fa:"\f068"}.fa-chess{--fa:"\f439"}.fa-arrow-left-long,.fa-long-arrow-left{--fa:"\f177"}.fa-plug-circle-check{--fa:"\e55c"}.fa-street-view{--fa:"\f21d"}.fa-franc-sign{--fa:"\e18f"}.fa-volume-off{--fa:"\f026"}.fa-american-sign-language-interpreting,.fa-asl-interpreting,.fa-hands-american-sign-language-interpreting,.fa-hands-asl-interpreting{--fa:"\f2a3"}.fa-cog,.fa-gear{--fa:"\f013"}.fa-droplet-slash,.fa-tint-slash{--fa:"\f5c7"}.fa-mosque{--fa:"\f678"}.fa-mosquito{--fa:"\e52b"}.fa-star-of-david{--fa:"\f69a"}.fa-person-military-rifle{--fa:"\e54b"}.fa-cart-shopping,.fa-shopping-cart{--fa:"\f07a"}.fa-vials{--fa:"\f493"}.fa-plug-circle-plus{--fa:"\e55f"}.fa-place-of-worship{--fa:"\f67f"}.fa-grip-vertical{--fa:"\f58e"}.fa-hexagon-nodes{--fa:"\e699"}.fa-arrow-turn-up,.fa-level-up{--fa:"\f148"}.fa-u{--fa:"\55"}.fa-square-root-alt,.fa-square-root-variable{--fa:"\f698"}.fa-clock,.fa-clock-four{--fa:"\f017"}.fa-backward-step,.fa-step-backward{--fa:"\f048"}.fa-pallet{--fa:"\f482"}.fa-faucet{--fa:"\e005"}.fa-baseball-bat-ball{--fa:"\f432"}.fa-s{--fa:"\53"}.fa-timeline{--fa:"\e29c"}.fa-keyboard{--fa:"\f11c"}.fa-caret-down{--fa:"\f0d7"}.fa-clinic-medical,.fa-house-chimney-medical{--fa:"\f7f2"}.fa-temperature-3,.fa-temperature-three-quarters,.fa-thermometer-3,.fa-thermometer-three-quarters{--fa:"\f2c8"}.fa-mobile-android-alt,.fa-mobile-screen{--fa:"\f3cf"}.fa-plane-up{--fa:"\e22d"}.fa-piggy-bank{--fa:"\f4d3"}.fa-battery-3,.fa-battery-half{--fa:"\f242"}.fa-mountain-city{--fa:"\e52e"}.fa-coins{--fa:"\f51e"}.fa-khanda{--fa:"\f66d"}.fa-sliders,.fa-sliders-h{--fa:"\f1de"}.fa-folder-tree{--fa:"\f802"}.fa-network-wired{--fa:"\f6ff"}.fa-map-pin{--fa:"\f276"}.fa-hamsa{--fa:"\f665"}.fa-cent-sign{--fa:"\e3f5"}.fa-flask{--fa:"\f0c3"}.fa-person-pregnant{--fa:"\e31e"}.fa-wand-sparkles{--fa:"\f72b"}.fa-ellipsis-v,.fa-ellipsis-vertical{--fa:"\f142"}.fa-ticket{--fa:"\f145"}.fa-power-off{--fa:"\f011"}.fa-long-arrow-alt-right,.fa-right-long{--fa:"\f30b"}.fa-flag-usa{--fa:"\f74d"}.fa-laptop-file{--fa:"\e51d"}.fa-teletype,.fa-tty{--fa:"\f1e4"}.fa-diagram-next{--fa:"\e476"}.fa-person-rifle{--fa:"\e54e"}.fa-house-medical-circle-exclamation{--fa:"\e512"}.fa-closed-captioning{--fa:"\f20a"}.fa-hiking,.fa-person-hiking{--fa:"\f6ec"}.fa-venus-double{--fa:"\f226"}.fa-images{--fa:"\f302"}.fa-calculator{--fa:"\f1ec"}.fa-people-pulling{--fa:"\e535"}.fa-n{--fa:"\4e"}.fa-cable-car,.fa-tram{--fa:"\f7da"}.fa-cloud-rain{--fa:"\f73d"}.fa-building-circle-xmark{--fa:"\e4d4"}.fa-ship{--fa:"\f21a"}.fa-arrows-down-to-line{--fa:"\e4b8"}.fa-download{--fa:"\f019"}.fa-face-grin,.fa-grin{--fa:"\f580"}.fa-backspace,.fa-delete-left{--fa:"\f55a"}.fa-eye-dropper,.fa-eye-dropper-empty,.fa-eyedropper{--fa:"\f1fb"}.fa-file-circle-check{--fa:"\e5a0"}.fa-forward{--fa:"\f04e"}.fa-mobile,.fa-mobile-android,.fa-mobile-phone{--fa:"\f3ce"}.fa-face-meh,.fa-meh{--fa:"\f11a"}.fa-align-center{--fa:"\f037"}.fa-book-dead,.fa-book-skull{--fa:"\f6b7"}.fa-drivers-license,.fa-id-card{--fa:"\f2c2"}.fa-dedent,.fa-outdent{--fa:"\f03b"}.fa-heart-circle-exclamation{--fa:"\e4fe"}.fa-home,.fa-home-alt,.fa-home-lg-alt,.fa-house{--fa:"\f015"}.fa-calendar-week{--fa:"\f784"}.fa-laptop-medical{--fa:"\f812"}.fa-b{--fa:"\42"}.fa-file-medical{--fa:"\f477"}.fa-dice-one{--fa:"\f525"}.fa-kiwi-bird{--fa:"\f535"}.fa-arrow-right-arrow-left,.fa-exchange{--fa:"\f0ec"}.fa-redo-alt,.fa-rotate-forward,.fa-rotate-right{--fa:"\f2f9"}.fa-cutlery,.fa-utensils{--fa:"\f2e7"}.fa-arrow-up-wide-short,.fa-sort-amount-up{--fa:"\f161"}.fa-mill-sign{--fa:"\e1ed"}.fa-bowl-rice{--fa:"\e2eb"}.fa-skull{--fa:"\f54c"}.fa-broadcast-tower,.fa-tower-broadcast{--fa:"\f519"}.fa-truck-pickup{--fa:"\f63c"}.fa-long-arrow-alt-up,.fa-up-long{--fa:"\f30c"}.fa-stop{--fa:"\f04d"}.fa-code-merge{--fa:"\f387"}.fa-upload{--fa:"\f093"}.fa-hurricane{--fa:"\f751"}.fa-mound{--fa:"\e52d"}.fa-toilet-portable{--fa:"\e583"}.fa-compact-disc{--fa:"\f51f"}.fa-file-arrow-down,.fa-file-download{--fa:"\f56d"}.fa-caravan{--fa:"\f8ff"}.fa-shield-cat{--fa:"\e572"}.fa-bolt,.fa-zap{--fa:"\f0e7"}.fa-glass-water{--fa:"\e4f4"}.fa-oil-well{--fa:"\e532"}.fa-vault{--fa:"\e2c5"}.fa-mars{--fa:"\f222"}.fa-toilet{--fa:"\f7d8"}.fa-plane-circle-xmark{--fa:"\e557"}.fa-cny,.fa-jpy,.fa-rmb,.fa-yen,.fa-yen-sign{--fa:"\f157"}.fa-rouble,.fa-rub,.fa-ruble,.fa-ruble-sign{--fa:"\f158"}.fa-sun{--fa:"\f185"}.fa-guitar{--fa:"\f7a6"}.fa-face-laugh-wink,.fa-laugh-wink{--fa:"\f59c"}.fa-horse-head{--fa:"\f7ab"}.fa-bore-hole{--fa:"\e4c3"}.fa-industry{--fa:"\f275"}.fa-arrow-alt-circle-down,.fa-circle-down{--fa:"\f358"}.fa-arrows-turn-to-dots{--fa:"\e4c1"}.fa-florin-sign{--fa:"\e184"}.fa-arrow-down-short-wide,.fa-sort-amount-desc,.fa-sort-amount-down-alt{--fa:"\f884"}.fa-less-than{--fa:"\3c"}.fa-angle-down{--fa:"\f107"}.fa-car-tunnel{--fa:"\e4de"}.fa-head-side-cough{--fa:"\e061"}.fa-grip-lines{--fa:"\f7a4"}.fa-thumbs-down{--fa:"\f165"}.fa-user-lock{--fa:"\f502"}.fa-arrow-right-long,.fa-long-arrow-right{--fa:"\f178"}.fa-anchor-circle-xmark{--fa:"\e4ac"}.fa-ellipsis,.fa-ellipsis-h{--fa:"\f141"}.fa-chess-pawn{--fa:"\f443"}.fa-first-aid,.fa-kit-medical{--fa:"\f479"}.fa-person-through-window{--fa:"\e5a9"}.fa-toolbox{--fa:"\f552"}.fa-hands-holding-circle{--fa:"\e4fb"}.fa-bug{--fa:"\f188"}.fa-credit-card,.fa-credit-card-alt{--fa:"\f09d"}.fa-automobile,.fa-car{--fa:"\f1b9"}.fa-hand-holding-hand{--fa:"\e4f7"}.fa-book-open-reader,.fa-book-reader{--fa:"\f5da"}.fa-mountain-sun{--fa:"\e52f"}.fa-arrows-left-right-to-line{--fa:"\e4ba"}.fa-dice-d20{--fa:"\f6cf"}.fa-truck-droplet{--fa:"\e58c"}.fa-file-circle-xmark{--fa:"\e5a1"}.fa-temperature-arrow-up,.fa-temperature-up{--fa:"\e040"}.fa-medal{--fa:"\f5a2"}.fa-bed{--fa:"\f236"}.fa-h-square,.fa-square-h{--fa:"\f0fd"}.fa-podcast{--fa:"\f2ce"}.fa-temperature-4,.fa-temperature-full,.fa-thermometer-4,.fa-thermometer-full{--fa:"\f2c7"}.fa-bell{--fa:"\f0f3"}.fa-superscript{--fa:"\f12b"}.fa-plug-circle-xmark{--fa:"\e560"}.fa-star-of-life{--fa:"\f621"}.fa-phone-slash{--fa:"\f3dd"}.fa-paint-roller{--fa:"\f5aa"}.fa-hands-helping,.fa-handshake-angle{--fa:"\f4c4"}.fa-location-dot,.fa-map-marker-alt{--fa:"\f3c5"}.fa-file{--fa:"\f15b"}.fa-greater-than{--fa:"\3e"}.fa-person-swimming,.fa-swimmer{--fa:"\f5c4"}.fa-arrow-down{--fa:"\f063"}.fa-droplet,.fa-tint{--fa:"\f043"}.fa-eraser{--fa:"\f12d"}.fa-earth,.fa-earth-america,.fa-earth-americas,.fa-globe-americas{--fa:"\f57d"}.fa-person-burst{--fa:"\e53b"}.fa-dove{--fa:"\f4ba"}.fa-battery-0,.fa-battery-empty{--fa:"\f244"}.fa-socks{--fa:"\f696"}.fa-inbox{--fa:"\f01c"}.fa-section{--fa:"\e447"}.fa-gauge-high,.fa-tachometer-alt,.fa-tachometer-alt-fast{--fa:"\f625"}.fa-envelope-open-text{--fa:"\f658"}.fa-hospital,.fa-hospital-alt,.fa-hospital-wide{--fa:"\f0f8"}.fa-wine-bottle{--fa:"\f72f"}.fa-chess-rook{--fa:"\f447"}.fa-bars-staggered,.fa-reorder,.fa-stream{--fa:"\f550"}.fa-dharmachakra{--fa:"\f655"}.fa-hotdog{--fa:"\f80f"}.fa-blind,.fa-person-walking-with-cane{--fa:"\f29d"}.fa-drum{--fa:"\f569"}.fa-ice-cream{--fa:"\f810"}.fa-heart-circle-bolt{--fa:"\e4fc"}.fa-fax{--fa:"\f1ac"}.fa-paragraph{--fa:"\f1dd"}.fa-check-to-slot,.fa-vote-yea{--fa:"\f772"}.fa-star-half{--fa:"\f089"}.fa-boxes,.fa-boxes-alt,.fa-boxes-stacked{--fa:"\f468"}.fa-chain,.fa-link{--fa:"\f0c1"}.fa-assistive-listening-systems,.fa-ear-listen{--fa:"\f2a2"}.fa-tree-city{--fa:"\e587"}.fa-play{--fa:"\f04b"}.fa-font{--fa:"\f031"}.fa-table-cells-row-lock{--fa:"\e67a"}.fa-rupiah-sign{--fa:"\e23d"}.fa-magnifying-glass,.fa-search{--fa:"\f002"}.fa-ping-pong-paddle-ball,.fa-table-tennis,.fa-table-tennis-paddle-ball{--fa:"\f45d"}.fa-diagnoses,.fa-person-dots-from-line{--fa:"\f470"}.fa-trash-can-arrow-up,.fa-trash-restore-alt{--fa:"\f82a"}.fa-naira-sign{--fa:"\e1f6"}.fa-cart-arrow-down{--fa:"\f218"}.fa-walkie-talkie{--fa:"\f8ef"}.fa-file-edit,.fa-file-pen{--fa:"\f31c"}.fa-receipt{--fa:"\f543"}.fa-pen-square,.fa-pencil-square,.fa-square-pen{--fa:"\f14b"}.fa-suitcase-rolling{--fa:"\f5c1"}.fa-person-circle-exclamation{--fa:"\e53f"}.fa-chevron-down{--fa:"\f078"}.fa-battery,.fa-battery-5,.fa-battery-full{--fa:"\f240"}.fa-skull-crossbones{--fa:"\f714"}.fa-code-compare{--fa:"\e13a"}.fa-list-dots,.fa-list-ul{--fa:"\f0ca"}.fa-school-lock{--fa:"\e56f"}.fa-tower-cell{--fa:"\e585"}.fa-down-long,.fa-long-arrow-alt-down{--fa:"\f309"}.fa-ranking-star{--fa:"\e561"}.fa-chess-king{--fa:"\f43f"}.fa-person-harassing{--fa:"\e549"}.fa-brazilian-real-sign{--fa:"\e46c"}.fa-landmark-alt,.fa-landmark-dome{--fa:"\f752"}.fa-arrow-up{--fa:"\f062"}.fa-television,.fa-tv,.fa-tv-alt{--fa:"\f26c"}.fa-shrimp{--fa:"\e448"}.fa-list-check,.fa-tasks{--fa:"\f0ae"}.fa-jug-detergent{--fa:"\e519"}.fa-circle-user,.fa-user-circle{--fa:"\f2bd"}.fa-user-shield{--fa:"\f505"}.fa-wind{--fa:"\f72e"}.fa-car-burst,.fa-car-crash{--fa:"\f5e1"}.fa-y{--fa:"\59"}.fa-person-snowboarding,.fa-snowboarding{--fa:"\f7ce"}.fa-shipping-fast,.fa-truck-fast{--fa:"\f48b"}.fa-fish{--fa:"\f578"}.fa-user-graduate{--fa:"\f501"}.fa-adjust,.fa-circle-half-stroke{--fa:"\f042"}.fa-clapperboard{--fa:"\e131"}.fa-circle-radiation,.fa-radiation-alt{--fa:"\f7ba"}.fa-baseball,.fa-baseball-ball{--fa:"\f433"}.fa-jet-fighter-up{--fa:"\e518"}.fa-diagram-project,.fa-project-diagram{--fa:"\f542"}.fa-copy{--fa:"\f0c5"}.fa-volume-mute,.fa-volume-times,.fa-volume-xmark{--fa:"\f6a9"}.fa-hand-sparkles{--fa:"\e05d"}.fa-grip,.fa-grip-horizontal{--fa:"\f58d"}.fa-share-from-square,.fa-share-square{--fa:"\f14d"}.fa-child-combatant,.fa-child-rifle{--fa:"\e4e0"}.fa-gun{--fa:"\e19b"}.fa-phone-square,.fa-square-phone{--fa:"\f098"}.fa-add,.fa-plus{--fa:"\2b"}.fa-expand{--fa:"\f065"}.fa-computer{--fa:"\e4e5"}.fa-close,.fa-multiply,.fa-remove,.fa-times,.fa-xmark{--fa:"\f00d"}.fa-arrows,.fa-arrows-up-down-left-right{--fa:"\f047"}.fa-chalkboard-teacher,.fa-chalkboard-user{--fa:"\f51c"}.fa-peso-sign{--fa:"\e222"}.fa-building-shield{--fa:"\e4d8"}.fa-baby{--fa:"\f77c"}.fa-users-line{--fa:"\e592"}.fa-quote-left,.fa-quote-left-alt{--fa:"\f10d"}.fa-tractor{--fa:"\f722"}.fa-trash-arrow-up,.fa-trash-restore{--fa:"\f829"}.fa-arrow-down-up-lock{--fa:"\e4b0"}.fa-lines-leaning{--fa:"\e51e"}.fa-ruler-combined{--fa:"\f546"}.fa-copyright{--fa:"\f1f9"}.fa-equals{--fa:"\3d"}.fa-blender{--fa:"\f517"}.fa-teeth{--fa:"\f62e"}.fa-ils,.fa-shekel,.fa-shekel-sign,.fa-sheqel,.fa-sheqel-sign{--fa:"\f20b"}.fa-map{--fa:"\f279"}.fa-rocket{--fa:"\f135"}.fa-photo-film,.fa-photo-video{--fa:"\f87c"}.fa-folder-minus{--fa:"\f65d"}.fa-hexagon-nodes-bolt{--fa:"\e69a"}.fa-store{--fa:"\f54e"}.fa-arrow-trend-up{--fa:"\e098"}.fa-plug-circle-minus{--fa:"\e55e"}.fa-sign,.fa-sign-hanging{--fa:"\f4d9"}.fa-bezier-curve{--fa:"\f55b"}.fa-bell-slash{--fa:"\f1f6"}.fa-tablet,.fa-tablet-android{--fa:"\f3fb"}.fa-school-flag{--fa:"\e56e"}.fa-fill{--fa:"\f575"}.fa-angle-up{--fa:"\f106"}.fa-drumstick-bite{--fa:"\f6d7"}.fa-holly-berry{--fa:"\f7aa"}.fa-chevron-left{--fa:"\f053"}.fa-bacteria{--fa:"\e059"}.fa-hand-lizard{--fa:"\f258"}.fa-notdef{--fa:"\e1fe"}.fa-disease{--fa:"\f7fa"}.fa-briefcase-medical{--fa:"\f469"}.fa-genderless{--fa:"\f22d"}.fa-chevron-right{--fa:"\f054"}.fa-retweet{--fa:"\f079"}.fa-car-alt,.fa-car-rear{--fa:"\f5de"}.fa-pump-soap{--fa:"\e06b"}.fa-video-slash{--fa:"\f4e2"}.fa-battery-2,.fa-battery-quarter{--fa:"\f243"}.fa-radio{--fa:"\f8d7"}.fa-baby-carriage,.fa-carriage-baby{--fa:"\f77d"}.fa-traffic-light{--fa:"\f637"}.fa-thermometer{--fa:"\f491"}.fa-vr-cardboard{--fa:"\f729"}.fa-hand-middle-finger{--fa:"\f806"}.fa-percent,.fa-percentage{--fa:"\25"}.fa-truck-moving{--fa:"\f4df"}.fa-glass-water-droplet{--fa:"\e4f5"}.fa-display{--fa:"\e163"}.fa-face-smile,.fa-smile{--fa:"\f118"}.fa-thumb-tack,.fa-thumbtack{--fa:"\f08d"}.fa-trophy{--fa:"\f091"}.fa-person-praying,.fa-pray{--fa:"\f683"}.fa-hammer{--fa:"\f6e3"}.fa-hand-peace{--fa:"\f25b"}.fa-rotate,.fa-sync-alt{--fa:"\f2f1"}.fa-spinner{--fa:"\f110"}.fa-robot{--fa:"\f544"}.fa-peace{--fa:"\f67c"}.fa-cogs,.fa-gears{--fa:"\f085"}.fa-warehouse{--fa:"\f494"}.fa-arrow-up-right-dots{--fa:"\e4b7"}.fa-splotch{--fa:"\f5bc"}.fa-face-grin-hearts,.fa-grin-hearts{--fa:"\f584"}.fa-dice-four{--fa:"\f524"}.fa-sim-card{--fa:"\f7c4"}.fa-transgender,.fa-transgender-alt{--fa:"\f225"}.fa-mercury{--fa:"\f223"}.fa-arrow-turn-down,.fa-level-down{--fa:"\f149"}.fa-person-falling-burst{--fa:"\e547"}.fa-award{--fa:"\f559"}.fa-ticket-alt,.fa-ticket-simple{--fa:"\f3ff"}.fa-building{--fa:"\f1ad"}.fa-angle-double-left,.fa-angles-left{--fa:"\f100"}.fa-qrcode{--fa:"\f029"}.fa-clock-rotate-left,.fa-history{--fa:"\f1da"}.fa-face-grin-beam-sweat,.fa-grin-beam-sweat{--fa:"\f583"}.fa-arrow-right-from-file,.fa-file-export{--fa:"\f56e"}.fa-shield,.fa-shield-blank{--fa:"\f132"}.fa-arrow-up-short-wide,.fa-sort-amount-up-alt{--fa:"\f885"}.fa-comment-nodes{--fa:"\e696"}.fa-house-medical{--fa:"\e3b2"}.fa-golf-ball,.fa-golf-ball-tee{--fa:"\f450"}.fa-chevron-circle-left,.fa-circle-chevron-left{--fa:"\f137"}.fa-house-chimney-window{--fa:"\e00d"}.fa-pen-nib{--fa:"\f5ad"}.fa-tent-arrow-turn-left{--fa:"\e580"}.fa-tents{--fa:"\e582"}.fa-magic,.fa-wand-magic{--fa:"\f0d0"}.fa-dog{--fa:"\f6d3"}.fa-carrot{--fa:"\f787"}.fa-moon{--fa:"\f186"}.fa-wine-glass-alt,.fa-wine-glass-empty{--fa:"\f5ce"}.fa-cheese{--fa:"\f7ef"}.fa-yin-yang{--fa:"\f6ad"}.fa-music{--fa:"\f001"}.fa-code-commit{--fa:"\f386"}.fa-temperature-low{--fa:"\f76b"}.fa-biking,.fa-person-biking{--fa:"\f84a"}.fa-broom{--fa:"\f51a"}.fa-shield-heart{--fa:"\e574"}.fa-gopuram{--fa:"\f664"}.fa-earth-oceania,.fa-globe-oceania{--fa:"\e47b"}.fa-square-xmark,.fa-times-square,.fa-xmark-square{--fa:"\f2d3"}.fa-hashtag{--fa:"\23"}.fa-expand-alt,.fa-up-right-and-down-left-from-center{--fa:"\f424"}.fa-oil-can{--fa:"\f613"}.fa-t{--fa:"\54"}.fa-hippo{--fa:"\f6ed"}.fa-chart-column{--fa:"\e0e3"}.fa-infinity{--fa:"\f534"}.fa-vial-circle-check{--fa:"\e596"}.fa-person-arrow-down-to-line{--fa:"\e538"}.fa-voicemail{--fa:"\f897"}.fa-fan{--fa:"\f863"}.fa-person-walking-luggage{--fa:"\e554"}.fa-arrows-alt-v,.fa-up-down{--fa:"\f338"}.fa-cloud-moon-rain{--fa:"\f73c"}.fa-calendar{--fa:"\f133"}.fa-trailer{--fa:"\e041"}.fa-bahai,.fa-haykal{--fa:"\f666"}.fa-sd-card{--fa:"\f7c2"}.fa-dragon{--fa:"\f6d5"}.fa-shoe-prints{--fa:"\f54b"}.fa-circle-plus,.fa-plus-circle{--fa:"\f055"}.fa-face-grin-tongue-wink,.fa-grin-tongue-wink{--fa:"\f58b"}.fa-hand-holding{--fa:"\f4bd"}.fa-plug-circle-exclamation{--fa:"\e55d"}.fa-chain-broken,.fa-chain-slash,.fa-link-slash,.fa-unlink{--fa:"\f127"}.fa-clone{--fa:"\f24d"}.fa-person-walking-arrow-loop-left{--fa:"\e551"}.fa-arrow-up-z-a,.fa-sort-alpha-up-alt{--fa:"\f882"}.fa-fire-alt,.fa-fire-flame-curved{--fa:"\f7e4"}.fa-tornado{--fa:"\f76f"}.fa-file-circle-plus{--fa:"\e494"}.fa-book-quran,.fa-quran{--fa:"\f687"}.fa-anchor{--fa:"\f13d"}.fa-border-all{--fa:"\f84c"}.fa-angry,.fa-face-angry{--fa:"\f556"}.fa-cookie-bite{--fa:"\f564"}.fa-arrow-trend-down{--fa:"\e097"}.fa-feed,.fa-rss{--fa:"\f09e"}.fa-draw-polygon{--fa:"\f5ee"}.fa-balance-scale,.fa-scale-balanced{--fa:"\f24e"}.fa-gauge-simple-high,.fa-tachometer,.fa-tachometer-fast{--fa:"\f62a"}.fa-shower{--fa:"\f2cc"}.fa-desktop,.fa-desktop-alt{--fa:"\f390"}.fa-m{--fa:"\4d"}.fa-table-list,.fa-th-list{--fa:"\f00b"}.fa-comment-sms,.fa-sms{--fa:"\f7cd"}.fa-book{--fa:"\f02d"}.fa-user-plus{--fa:"\f234"}.fa-check{--fa:"\f00c"}.fa-battery-4,.fa-battery-three-quarters{--fa:"\f241"}.fa-house-circle-check{--fa:"\e509"}.fa-angle-left{--fa:"\f104"}.fa-diagram-successor{--fa:"\e47a"}.fa-truck-arrow-right{--fa:"\e58b"}.fa-arrows-split-up-and-left{--fa:"\e4bc"}.fa-fist-raised,.fa-hand-fist{--fa:"\f6de"}.fa-cloud-moon{--fa:"\f6c3"}.fa-briefcase{--fa:"\f0b1"}.fa-person-falling{--fa:"\e546"}.fa-image-portrait,.fa-portrait{--fa:"\f3e0"}.fa-user-tag{--fa:"\f507"}.fa-rug{--fa:"\e569"}.fa-earth-europe,.fa-globe-europe{--fa:"\f7a2"}.fa-cart-flatbed-suitcase,.fa-luggage-cart{--fa:"\f59d"}.fa-rectangle-times,.fa-rectangle-xmark,.fa-times-rectangle,.fa-window-close{--fa:"\f410"}.fa-baht-sign{--fa:"\e0ac"}.fa-book-open{--fa:"\f518"}.fa-book-journal-whills,.fa-journal-whills{--fa:"\f66a"}.fa-handcuffs{--fa:"\e4f8"}.fa-exclamation-triangle,.fa-triangle-exclamation,.fa-warning{--fa:"\f071"}.fa-database{--fa:"\f1c0"}.fa-mail-forward,.fa-share{--fa:"\f064"}.fa-bottle-droplet{--fa:"\e4c4"}.fa-mask-face{--fa:"\e1d7"}.fa-hill-rockslide{--fa:"\e508"}.fa-exchange-alt,.fa-right-left{--fa:"\f362"}.fa-paper-plane{--fa:"\f1d8"}.fa-road-circle-exclamation{--fa:"\e565"}.fa-dungeon{--fa:"\f6d9"}.fa-align-right{--fa:"\f038"}.fa-money-bill-1-wave,.fa-money-bill-wave-alt{--fa:"\f53b"}.fa-life-ring{--fa:"\f1cd"}.fa-hands,.fa-sign-language,.fa-signing{--fa:"\f2a7"}.fa-calendar-day{--fa:"\f783"}.fa-ladder-water,.fa-swimming-pool,.fa-water-ladder{--fa:"\f5c5"}.fa-arrows-up-down,.fa-arrows-v{--fa:"\f07d"}.fa-face-grimace,.fa-grimace{--fa:"\f57f"}.fa-wheelchair-alt,.fa-wheelchair-move{--fa:"\e2ce"}.fa-level-down-alt,.fa-turn-down{--fa:"\f3be"}.fa-person-walking-arrow-right{--fa:"\e552"}.fa-envelope-square,.fa-square-envelope{--fa:"\f199"}.fa-dice{--fa:"\f522"}.fa-bowling-ball{--fa:"\f436"}.fa-brain{--fa:"\f5dc"}.fa-band-aid,.fa-bandage{--fa:"\f462"}.fa-calendar-minus{--fa:"\f272"}.fa-circle-xmark,.fa-times-circle,.fa-xmark-circle{--fa:"\f057"}.fa-gifts{--fa:"\f79c"}.fa-hotel{--fa:"\f594"}.fa-earth-asia,.fa-globe-asia{--fa:"\f57e"}.fa-id-card-alt,.fa-id-card-clip{--fa:"\f47f"}.fa-magnifying-glass-plus,.fa-search-plus{--fa:"\f00e"}.fa-thumbs-up{--fa:"\f164"}.fa-user-clock{--fa:"\f4fd"}.fa-allergies,.fa-hand-dots{--fa:"\f461"}.fa-file-invoice{--fa:"\f570"}.fa-window-minimize{--fa:"\f2d1"}.fa-coffee,.fa-mug-saucer{--fa:"\f0f4"}.fa-brush{--fa:"\f55d"}.fa-file-half-dashed{--fa:"\e698"}.fa-mask{--fa:"\f6fa"}.fa-magnifying-glass-minus,.fa-search-minus{--fa:"\f010"}.fa-ruler-vertical{--fa:"\f548"}.fa-user-alt,.fa-user-large{--fa:"\f406"}.fa-train-tram{--fa:"\e5b4"}.fa-user-nurse{--fa:"\f82f"}.fa-syringe{--fa:"\f48e"}.fa-cloud-sun{--fa:"\f6c4"}.fa-stopwatch-20{--fa:"\e06f"}.fa-square-full{--fa:"\f45c"}.fa-magnet{--fa:"\f076"}.fa-jar{--fa:"\e516"}.fa-note-sticky,.fa-sticky-note{--fa:"\f249"}.fa-bug-slash{--fa:"\e490"}.fa-arrow-up-from-water-pump{--fa:"\e4b6"}.fa-bone{--fa:"\f5d7"}.fa-table-cells-row-unlock{--fa:"\e691"}.fa-user-injured{--fa:"\f728"}.fa-face-sad-tear,.fa-sad-tear{--fa:"\f5b4"}.fa-plane{--fa:"\f072"}.fa-tent-arrows-down{--fa:"\e581"}.fa-exclamation{--fa:"\21"}.fa-arrows-spin{--fa:"\e4bb"}.fa-print{--fa:"\f02f"}.fa-try,.fa-turkish-lira,.fa-turkish-lira-sign{--fa:"\e2bb"}.fa-dollar,.fa-dollar-sign,.fa-usd{--fa:"\24"}.fa-x{--fa:"\58"}.fa-magnifying-glass-dollar,.fa-search-dollar{--fa:"\f688"}.fa-users-cog,.fa-users-gear{--fa:"\f509"}.fa-person-military-pointing{--fa:"\e54a"}.fa-bank,.fa-building-columns,.fa-institution,.fa-museum,.fa-university{--fa:"\f19c"}.fa-umbrella{--fa:"\f0e9"}.fa-trowel{--fa:"\e589"}.fa-d{--fa:"\44"}.fa-stapler{--fa:"\e5af"}.fa-masks-theater,.fa-theater-masks{--fa:"\f630"}.fa-kip-sign{--fa:"\e1c4"}.fa-hand-point-left{--fa:"\f0a5"}.fa-handshake-alt,.fa-handshake-simple{--fa:"\f4c6"}.fa-fighter-jet,.fa-jet-fighter{--fa:"\f0fb"}.fa-share-alt-square,.fa-square-share-nodes{--fa:"\f1e1"}.fa-barcode{--fa:"\f02a"}.fa-plus-minus{--fa:"\e43c"}.fa-video,.fa-video-camera{--fa:"\f03d"}.fa-graduation-cap,.fa-mortar-board{--fa:"\f19d"}.fa-hand-holding-medical{--fa:"\e05c"}.fa-person-circle-check{--fa:"\e53e"}.fa-level-up-alt,.fa-turn-up{--fa:"\f3bf"} +.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0} \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/css/solid.css b/src/WebExpress.WebUI/Assets/css/solid.css index ab3588bc..dc22a275 100644 --- a/src/WebExpress.WebUI/Assets/css/solid.css +++ b/src/WebExpress.WebUI/Assets/css/solid.css @@ -1,16 +1,22 @@ /*! - * Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com + * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2024 Fonticons, Inc. */ +:root, :host { + --fa-style-family-classic: 'Font Awesome 6 Free'; + --fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; +} + @font-face { - font-family: 'Font Awesome 5 Free'; - font-style: normal; - font-weight: 900; - font-display: block; - src: url("../fonts/fa-solid-900.eot"); - src: url("../fonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../fonts/fa-solid-900.woff2") format("woff2"), url("../fonts/fa-solid-900.woff") format("woff"), url("../fonts/fa-solid-900.ttf") format("truetype"), url("../fonts/fa-solid-900.svg#fontawesome") format("svg"); } + font-family: 'Font Awesome 6 Free'; + font-style: normal; + font-weight: 900; + font-display: block; + src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); +} -.fa, -.fas { - font-family: 'Font Awesome 5 Free'; - font-weight: 900; } +.fas, +.fa-solid { + font-weight: 900; +} diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.button.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.button.css new file mode 100644 index 00000000..e69de29b diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.css index 07be8dd9..67bc360c 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.css @@ -15,10 +15,10 @@ body, html { display: none; } -.profile { +.wx-profile { } - .profile img { + .wx-profile img { width: 40px; height: 40px; float: left; @@ -27,7 +27,7 @@ body, html { border-radius: 50%; } - .profile b { + .wx-profile b { width: 40px; height: 40px; float: left; @@ -38,67 +38,67 @@ body, html { line-height: 40px; } -.timeline { +.wx-timeline { list-style: none; margin: 0px; padding-left: 10px } - .timeline .item { + .wx-timeline .item { border: none; margin-top: 5px; margin-bottom: 20px; padding: 5px; } - .timeline .item .header { + .wx-timeline .item .header { width: 100%; } - .timeline .item .header p span { + .wx-timeline .item .header p span { margin: 0px; color: gray; font-weight: normal; } - .timeline .item .header small { + .wx-timeline .item .header small { margin: 0px; color: gray; } - .timeline .item .post { + .wx-timeline .item .post { padding: 5px 5px 15px 5px; position: relative; } - .timeline .item .options { + .wx-timeline .item .options { margin-bottom: 10px; } - .timeline .item form { + .wx-timeline .item form { margin-left: 0px; margin-right: 50px; } - .timeline .item form textarea { + .wx-timeline .item form textarea { margin-bottom: 10px; } - .timeline .item form button { + .wx-timeline .item form button { display: inline-block; } - .timeline .item .comment { + .wx-timeline .item .comment { background-color: white; margin-left: 50px; } - .timeline .item .comment .header { + .wx-timeline .item .comment .header { border: none; margin-bottom: 10px; } - .timeline .item .comment .post { + .wx-timeline .item .comment .post { background-color: white; margin-left: 50px; margin-right: 0px; @@ -106,11 +106,11 @@ body, html { margin-bottom: 10px; } - .timeline .item .comment .options { + .wx-timeline .item .comment .options { margin-left: 50px; } -.callout { +.wx-callout { position: relative; display: -ms-flexbox; display: flex; @@ -124,61 +124,62 @@ body, html { border-radius: 0.25rem; } -.callout-body { +.wx-callout-body { -ms-flex: 1 1 auto; flex: 1 1 auto; padding: 1.25rem; border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); } -.callout-title { +.wx-callout-title { margin-top: 0.375rem; margin-bottom: 0.1rem; margin-left: 1.25rem; font-weight: bold; } -.callout-info { +.wx-callout-info { border-left-color: #31d2f2; background-color: #f8f9fa; } -.callout-success { +.wx-callout-success { border-left-color: #146c43; background-color: #f8f9fa; } -.callout-warning { +.wx-callout-warning { border-left-color: #ffcd39; background-color: #f8f9fa; } -.callout-danger { +.wx-callout-danger { border-left-color: #b02a37; background-color: #f8f9fa; } -.callout-primary { +.wx-callout-primary { border-left-color: #0a58ca; background-color: #f8f9fa; } -.callout-secondary { +.wx-callout-secondary { border-left-color: #6c757d; background-color: #f8f9fa; } -.callout-dark { +.wx-callout-dark { border-left-color: #1a1e21; background-color: #f8f9fa; } -.callout-light { +.wx-callout-light { border-left-color: #f9fafb; background-color: #f8f9fa; } -.split { +.wx-split-vertical { + flex-grow: 1; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; @@ -186,18 +187,24 @@ body, html { overflow-x: hidden; } -.splitter { +.wx-split-horizontal { + flex-grow: 1; + display: flex !important; + flex-direction: row; +} + +.wx-splitter { background-repeat: no-repeat; background-position: 50%; } - .splitter.splitter-horizontal { + .wx-splitter.wx-splitter-horizontal { float: left; cursor: ew-resize; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAkSURBVChTYzxz5kw/AxCYmJgUwthMIIJ8MGomEIyaidNMBgYANc83sUCD+ssAAAAASUVORK5CYII'); } - .splitter.splitter-vertical { + .wx-splitter.wx-splitter-vertical { cursor: ns-resize; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFCAYAAABSIVz6AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAhSURBVChTYzxz5kw/AxCYmJgUgmh68ZlAxIgCIy2oGRgAUTM97WZuFTcAAAAASUVORK5CYII'); } diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.dropdownbutton.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.dropdownbutton.css new file mode 100644 index 00000000..e69de29b diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.expand.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.expand.css index 47e14e34..1cac99f8 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.expand.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.expand.css @@ -8,25 +8,24 @@ } } -.expand { +.wx-expand { } - - .expand > a { + .wx-expand > a { cursor: default; text-decoration: none; margin-right: 0.8em; } - .expand > span { + .wx-expand > span { cursor: default; } -.expand-angle { - content:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIJdmlld0JveD0iMCAwIdEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCgkJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgsLTE4KSI+DQoJCQk8cGF0aCBkPSJNMS42NiAxNzMuMiBMMTguNyAxOTAuMjMgQTUuNjY5MjkgNS42NjkyOSAtMTgwIdAgMCAyNi43MiAxOTAuMjMgTDk4Ljg3IdExOC4wOCBBNS42NjkyOSA1LjY2OTI5IC0xODAgMCAwIdk4Ljg3DQoJCQkJCQkgMTEwLjA2IEwyNi43MiAzNy45IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMTguNyAzNy45IEwxLjY2IdU0Ljk0IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMS42NiA2Mi45Ng0KCQkJCQkJIEw0OC43NiAxMTAuMDYgQTUuNjY5MjkgNS42NjkyOSAwIdAgMSA0OC43NiAxMTguMDggTDEuNjYgMTY1LjE4IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMS42NiAxNzMuMiBaIg0KCQkJCQljbGFzcz0ic3QxIi8+DQoJCTwvZz4NCjwvc3ZnPg'); +.wx-expand-angle { + content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIJdmlld0JveD0iMCAwIDEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCgkJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgsLTE4KSI+DQoJCQk8cGF0aCBkPSJNMS42NiAxNzMuMiBMMTguNyAxOTAuMjMgQTUuNjY5MjkgNS42NjkyOSAtMTgwIDAgMCAyNi43MiAxOTAuMjMgTDk4Ljg3IDExOC4wOCBBNS42NjkyOSA1LjY2OTI5IC0xODAgMCAwIDk4Ljg3DQoJCQkJCQkgMTEwLjA2IEwyNi43MiAzNy45IEE1LjY2OTI5IDUuNjY5MjkgLTE4MCAwIDAgMTguNyAzNy45IEwxLjY2IDU0Ljk0IEE1LjY2OTI5IDUuNjY5MjkgLTE4MCAwIDAgMS42NiA2Mi45Ng0KCQkJCQkJIEw0OC43NiAxMTAuMDYgQTUuNjY5MjkgNS42NjkyOSAwIDAgMSA0OC43NiAxMTguMDggTDEuNjYgMTY1LjE4IEE1LjY2OTI5IDUuNjY5MjkgLTE4MCAwIDAgMS42NiAxNzMuMiBaIg0KCQkJCQljbGFzcz0ic3QxIi8+DQoJCTwvZz4NCjwvc3ZnPg=='); height: 0.8rem; } -.expand-angle-down { +.wx-expand-angle-down { transform: rotate(90deg); transform-origin: center; animation-name: expand-rotate; diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.form.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.form.css index 24b19d8e..74dc09b0 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.form.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.form.css @@ -1,108 +1,113 @@ -ο»Ώ.input-success { +ο»Ώ.wx-input-success { border-color: #28a745 !important; } - .input-success:focus { + .wx-input-success:focus { border-color: #28a745; box-shadow: 0px 0px 0px 0.2rem rgba(40, 167, 69, 0.25); } -.input-warning { +.wx-input-warning { border-color: #ffc107 !important; } - .input-warning:focus { + .wx-input-warning:focus { border-color: #ffc107; box-shadow: 0px 0px 0px 0.2rem rgba(207, 220, 0, 0.25); } -.input-error { +.wx-input-error { border-color: #dc3545 !important; } - .input-error:focus { + .wx-input-error:focus { border-color: #dc3545; box-shadow: 0px 0px 0px 0.2rem rgba(220, 53, 69, 0.25); } -.form-group-horizontal { +.wx-form-inline { + display: flex; + align-items: center; +} + +.wx-form-group-horizontal { display: table; table-layout: auto; border-spacing: 0.5em; width: 100%; } - .form-group-horizontal > div { + .wx-form-group-horizontal > div { display: table-row-group; } - .form-group-horizontal > div > div { + .wx-form-group-horizontal > div > div { display: table-row; } - .form-group-horizontal > div > div > div { + .wx-form-group-horizontal > div > div > div { display: table-cell; } - .form-group-horizontal > div > div > div:first-child { + .wx-form-group-horizontal > div > div > div:first-child { width: 10%; white-space: nowrap; vertical-align: top; padding-top: 0.4rem; } -.form-group-mix { +.wx-form-group-mix { display: table; table-layout: auto; border-spacing: 0.5em; width: 100%; } - .form-group-mix > div { + .wx-form-group-mix > div { display: table-row-group; } - .form-group-mix > div > div { + .wx-form-group-mix > div > div { display: table-row; } - .form-group-mix > div > div > div { + .wx-form-group-mix > div > div > div { display: table-cell; } - .form-group-mix > div > div > div:first-child { + .wx-form-group-mix > div > div > div:first-child { width: 10%; white-space: nowrap; } -.form-group-column { +.wx-form-group-column { display: flex; } - .form-group-column > div { + .wx-form-group-column > div { margin-right: 0.5em; } -.form-group-column-horizontal { +.wx-form-group-column-horizontal { display: flex; } - .form-group-column-horizontal > div { + .wx-form-group-column-horizontal > div { display: flex; margin-right: 0.5em; vertical-align: middle; } -.form-group-column-mix { +.wx-form-group-column-mix { display: flex; } - .form-group-column-mix > div { + .wx-form-group-column-mix > div { display: flex; margin-right: 0.5em; vertical-align: middle; } -form > footer { +wx-form > footer { margin-top: 0.5em; } diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalformular.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalform.css similarity index 69% rename from src/WebExpress.WebUI/Assets/css/webexpress.webui.modalformular.css rename to src/WebExpress.WebUI/Assets/css/webexpress.webui.modalform.css index 8072b5c6..cb570941 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalformular.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalform.css @@ -1,7 +1,6 @@ -.modalformular { - +.wx-modalform { } - .modalformular > .modal-dialog > form { + .wx-modalform > .modal-dialog > form { background-color: transparent !important; border: none !important; margin: 0 !important; diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalpage.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalpage.css index a3988e40..91991382 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalpage.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.modalpage.css @@ -1,7 +1,6 @@ -.modalpage { - +.wx-modalpage { } - .modalpage > .modal-dialog > .modal-content > form { + .wx-modalpage > .modal-dialog > .modal-content > form { background-color: transparent !important; margin: 0 !important; } \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.more.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.more.css index 5c0467c5..e69de29b 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.more.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.more.css @@ -1,35 +0,0 @@ -@keyframes expand-rotate { - 0% { - transform: rotate(0deg); - } - - 100% { - transform: rotate(90deg); - } -} - -.expand { -} - .expand > a { - cursor: default; - text-decoration: none; - margin-right: 0.8em; - } - - .expand > span { - cursor: default; - } - -.expand-angle { - #content:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIJdmlld0JveD0iMCAwIdEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCgkJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgsLTE4KSI+DQoJCQk8cGF0aCBkPSJNMS42NiAxNzMuMiBMMTguNyAxOTAuMjMgQTUuNjY5MjkgNS42NjkyOSAtMTgwIdAgMCAyNi43MiAxOTAuMjMgTDk4Ljg3IdExOC4wOCBBNS42NjkyOSA1LjY2OTI5IC0xODAgMCAwIdk4Ljg3DQoJCQkJCQkgMTEwLjA2IEwyNi43MiAzNy45IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMTguNyAzNy45IEwxLjY2IdU0Ljk0IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMS42NiA2Mi45Ng0KCQkJCQkJIEw0OC43NiAxMTAuMDYgQTUuNjY5MjkgNS42NjkyOSAwIdAgMSA0OC43NiAxMTguMDggTDEuNjYgMTY1LjE4IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMS42NiAxNzMuMiBaIg0KCQkJCQljbGFzcz0ic3QxIi8+DQoJCTwvZz4NCjwvc3ZnPg'); - height: 0.8rem; - padding-left: 0.1rem; - padding-right: 0.3rem; -} - -.expand-angle-down { - transform: rotate(90deg); - transform-origin: center; - animation-name: expand-rotate; - animation-duration: 0.3s; -} \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.move.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.move.css index 88953e7e..ad525384 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.move.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.move.css @@ -1,38 +1,38 @@ -.move { +.wx-move { display: flex; margin-bottom: 0.5em; padding: 2px; } -.move-list { +.wx-move-list { margin: 0px; width: 40%; } - .move-list > p { + .wx-move-list > p { cursor: default; padding-left: 0.5em; } - .move-list > ul { + .wx-move-list > ul { height: 250px; overflow-y: auto; cursor: default; } - .move-list > ul > li { + .wx-move-list > ul > li { cursor: pointer; } - .move-list > ul > li > img { + .wx-move-list > ul > li > img { height: 16px; margin-right: 0.5em; } - .move-list > ul > li > i { + .wx-move-list > ul > li > i { margin-right: 0.5em; -} + } -.move-button { +.wx-move-button { margin: auto; } \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.search.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.search.css index bd426a1c..ec0eea43 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.search.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.search.css @@ -1,4 +1,4 @@ -.search { +.wx-search { position: relative; border-radius: 4px; display: flex !important; @@ -7,23 +7,23 @@ align-items: center; } - .search > label { + .wx-search > label { margin-right: 0.5em; color: #cccccc; } - .search > input { + .wx-search > input { border: 0px; outline: none; flex-grow: 1 !important; } - .search > span { + .wx-search > span { margin-left: 0.5em; cursor: pointer; color: #cccccc; } - .search > span:hover { + .wx-search > span:hover { color: #aaaaaa; } \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.selection.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.selection.css index b179b1c1..7c758e2f 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.selection.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.selection.css @@ -1,9 +1,9 @@ -.selection { +.wx-selection { position: relative; padding: 0px; } - .selection > span { + .wx-selection > span { display: flex !important; flex-direction: row; flex-wrap: nowrap !important; @@ -12,13 +12,13 @@ min-height: 2.2em; } - .selection > span > ul { + .wx-selection > span > ul { padding-left: 0; margin-right: auto; margin-bottom: 0; } - .selection > span > ul > li { + .wx-selection > span > ul > li { float: left; display: flex; align-items: center; @@ -33,23 +33,23 @@ white-space: nowrap; } - .selection > span > ul > li > img { + .wx-selection > span > ul > li > img { height: 16px; vertical-align: middle; } - .selection > span > ul > li > a { + .wx-selection > span > ul > li > a { margin-left: 0.5em; margin-right: 0.1em; text-decoration: none; color: #cccccc; } - .selection > span > ul > li > a:hover { + .wx-selection > span > ul > li > a:hover { color: #aaaaaa; } - .selection > span > a { + .wx-selection > span > a { text-decoration: none; color: #cccccc; float: right; @@ -57,11 +57,11 @@ padding-right: 0.3em; } - .selection > span > a:hover { + .wx-selection > span > a:hover { color: #aaaaaa; } - .selection div > ul { + .wx-selection div > ul { overflow-x: hidden; overflow-y: scroll; height: 15rem; @@ -69,7 +69,7 @@ padding: 0px; } - .selection div > input { + .wx-selection div > input { margin-left: 0.2em; margin-right: 0.2em; margin-top: 0em; @@ -79,38 +79,38 @@ width: 99%; } - .selection div > ul > li > span > span { + .wx-selection div > ul > li > span > span { display: flex; align-items: center; } - .selection div > ul > li > span > span > img { + .wx-selection div > ul > li > span > span > img { height: 16px; margin-right: 0.5em; vertical-align: middle; } - .selection div > ul > li > a { + .wx-selection div > ul > li > a { cursor: default; color: #000000; } - .selection div > ul > li > small { + .wx-selection div > ul > li > small { margin-left: 0.5em; color: var(--bs-secondary); } - .selection div > ul > li > span > span > a { + .wx-selection div > ul > li > span > span > a { cursor: default; color: #000000; } - .selection div > ul > li > span > span > small { + .wx-selection div > ul > li > span > span > small { margin-left: 0.5em; color: var(--bs-secondary); } - .selection > div > ul > li > span > p { + .wx-selection > div > ul > li > span > p { white-space: break-spaces; cursor: default; } \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.toolpanel.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.toolpanel.css index 528d0fef..835a7cbc 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.toolpanel.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.toolpanel.css @@ -1,19 +1,19 @@ -.toolpanel { +.wx-toolpanel { display: flex; flex-direction: column; align-items: flex-end; width: fit-content; } - .toolpanel > .dropdown { + .wx-toolpanel > .dropdown { display: none; position: absolute; } - .toolpanel:hover .dropdown { + .wx-toolpanel:hover .dropdown { display: block; } - .toolpanel:focus .dropdown { + .wx-toolpanel:focus .dropdown { display: block; } diff --git a/src/WebExpress.WebUI/Assets/css/webexpress.webui.tree.css b/src/WebExpress.WebUI/Assets/css/webexpress.webui.tree.css index 54365e72..9ca08358 100644 --- a/src/WebExpress.WebUI/Assets/css/webexpress.webui.tree.css +++ b/src/WebExpress.WebUI/Assets/css/webexpress.webui.tree.css @@ -8,42 +8,43 @@ } } -.tree { +.wx-tree { list-style-type: none; padding-left: 0; } -.tree-indicator-dot { - content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIdEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMCwtNTgpIj4NCiAgICAgICAgPHBhdGggZD0iTTAgMTUzLjk2IEEzNy45MzU5IdM3LjkzNTkgMCAwIdEgNzUuODcgMTUzLjk2IEEzNy45MzU5IdM3LjkzNTkgMCAxIdEgMCAxNTMuOTYgWiIgY2xhc3M9InN0MSIvPg0KICAgIdwvZz4NCjwvc3ZnPg0K'); +.wx-tree-indicator-dot { + content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCiAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMCwtNTgpIj4NCiAgICAgICAgPHBhdGggZD0iTTAgMTUzLjk2IEEzNy45MzU5IDM3LjkzNTkgMCAwIDEgNzUuODcgMTUzLjk2IEEzNy45MzU5IDM3LjkzNTkgMCAxIDEgMCAxNTMuOTYgWiIgY2xhc3M9InN0MSIvPg0KICAgPC9nPg0KPC9zdmc+DQo='); height: 1em; } -.tree-indicator-angle { - content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIJdmlld0JveD0iMCAwIdEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCgkJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgsLTE4KSI+DQoJCQk8cGF0aCBkPSJNMS42NiAxNzMuMiBMMTguNyAxOTAuMjMgQTUuNjY5MjkgNS42NjkyOSAtMTgwIdAgMCAyNi43MiAxOTAuMjMgTDk4Ljg3IdExOC4wOCBBNS42NjkyOSA1LjY2OTI5IC0xODAgMCAwIdk4Ljg3DQoJCQkJCQkgMTEwLjA2IEwyNi43MiAzNy45IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMTguNyAzNy45IEwxLjY2IdU0Ljk0IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMS42NiA2Mi45Ng0KCQkJCQkJIEw0OC43NiAxMTAuMDYgQTUuNjY5MjkgNS42NjkyOSAwIdAgMSA0OC43NiAxMTguMDggTDEuNjYgMTY1LjE4IEE1LjY2OTI5IdUuNjY5MjkgLTE4MCAwIdAgMS42NiAxNzMuMiBaIg0KCQkJCQljbGFzcz0ic3QxIi8+DQoJCTwvZz4NCjwvc3ZnPg'); +.wx-tree-indicator-angle { + content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIJdmlld0JveD0iMCAwIDEzNSAxOTAiPg0KCTxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJPCFbQ0RBVEFbDQoJCS5zdDEge2ZpbGw6IzdmN2Y3ZjtzdHJva2U6bm9uZTt9DQoJXV0+DQoJPC9zdHlsZT4NCgkJPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgsLTE4KSI+DQoJCQk8cGF0aCBkPSJNMS42NiAxNzMuMiBMMTguNyAxOTAuMjMgQTUuNjY5MjkgNS42NjkyOSAtMTgwIDAgMCAyNi43MiAxOTAuMjMgTDk4Ljg3IDExOC4wOCBBNS42NjkyOSA1LjY2OTI5IC0xODAgMCAwIDk4Ljg3DQoJCQkJCQkgMTEwLjA2IEwyNi43MiAzNy45IEE1LjY2OTI5IDUuNjY5MjkgLTE4MCAwIDAgMTguNyAzNy45IEwxLjY2IDU0Ljk0IEE1LjY2OTI5IDUuNjY5MjkgLTE4MCAwIDAgMS42NiA2Mi45Ng0KCQkJCQkJIEw0OC43NiAxMTAuMDYgQTUuNjY5MjkgNS42NjkyOSAwIDAgMSA0OC43NiAxMTguMDggTDEuNjYgMTY1LjE4IEE1LjY2OTI5IDUuNjY5MjkgLTE4MCAwIDAgMS42NiAxNzMuMiBaIg0KCQkJCQljbGFzcz0ic3QxIi8+DQoJCTwvZz4NCjwvc3ZnPg=='); height: 0.8rem; } -.tree-expand { +.wx-tree-expand { transform: rotate(90deg); transform-origin: center; animation-name: tree-rotate; animation-duration: 0.2s; + white: 1em; } -.tree-node { +.wx-tree-node { cursor: default; list-style-type: none; text-decoration: none; margin-right: 0.8em; } - .tree-node > span { + .wx-tree-node > span { display: flex; align-items: center; } - .tree-node > span > i { + .wx-tree-node > span > i { margin-right: 0.3em; } - - .tree-node > span > *:nth-child(2) { + + .wx-tree-node > span > *:nth-child(2) { padding-left: 0.3rem; } diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.eot b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.eot deleted file mode 100644 index 958684e2..00000000 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.eot and /dev/null differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.svg b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.svg deleted file mode 100644 index 2b7cf17b..00000000 --- a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.svg +++ /dev/null @@ -1,3717 +0,0 @@ - - - - -Created by FontForge 20200314 at Mon Oct 5 09:50:45 2020 - By Robert Madole -Copyright (c) Font Awesome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.ttf b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.ttf index f0718251..0f82a836 100644 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.ttf and b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.ttf differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff deleted file mode 100644 index 277ab65b..00000000 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff and /dev/null differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff2 b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff2 index 47805d47..3c5cf97e 100644 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff2 and b/src/WebExpress.WebUI/Assets/fonts/fa-brands-400.woff2 differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.eot b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.eot deleted file mode 100644 index bef9f722..00000000 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.eot and /dev/null differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.svg b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.svg deleted file mode 100644 index bccc256b..00000000 --- a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.svg +++ /dev/null @@ -1,801 +0,0 @@ - - - - -Created by FontForge 20200314 at Mon Oct 5 09:50:45 2020 - By Robert Madole -Copyright (c) Font Awesome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.ttf b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.ttf index 659527a6..9ee1919d 100644 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.ttf and b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.ttf differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff deleted file mode 100644 index 31f44b2d..00000000 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff and /dev/null differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff2 b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff2 index 0332a9bf..57d91796 100644 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff2 and b/src/WebExpress.WebUI/Assets/fonts/fa-regular-400.woff2 differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.eot b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.eot deleted file mode 100644 index 5da4fa00..00000000 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.eot and /dev/null differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.svg b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.svg deleted file mode 100644 index 313b3118..00000000 --- a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.svg +++ /dev/null @@ -1,5028 +0,0 @@ - - - - -Created by FontForge 20200314 at Mon Oct 5 09:50:45 2020 - By Robert Madole -Copyright (c) Font Awesome - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.ttf b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.ttf index e0746084..1c10972e 100644 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.ttf and b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.ttf differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff deleted file mode 100644 index ef6b4474..00000000 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff and /dev/null differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff2 b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff2 index 120b3007..16721020 100644 Binary files a/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff2 and b/src/WebExpress.WebUI/Assets/fonts/fa-solid-900.woff2 differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-v4compatibility.ttf b/src/WebExpress.WebUI/Assets/fonts/fa-v4compatibility.ttf new file mode 100644 index 00000000..3bcb67ff Binary files /dev/null and b/src/WebExpress.WebUI/Assets/fonts/fa-v4compatibility.ttf differ diff --git a/src/WebExpress.WebUI/Assets/fonts/fa-v4compatibility.woff2 b/src/WebExpress.WebUI/Assets/fonts/fa-v4compatibility.woff2 new file mode 100644 index 00000000..fbafb222 Binary files /dev/null and b/src/WebExpress.WebUI/Assets/fonts/fa-v4compatibility.woff2 differ diff --git a/src/WebExpress.WebUI/Assets/js/bootstrap.min.js b/src/WebExpress.WebUI/Assets/js/bootstrap.min.js index 3b660bae..d5dc5ea1 100644 --- a/src/WebExpress.WebUI/Assets/js/bootstrap.min.js +++ b/src/WebExpress.WebUI/Assets/js/bootstrap.min.js @@ -1,7 +1,7 @@ /*! - * Bootstrap v5.3.2 (https://getbootstrap.com/) - * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ -!function (t, e) { "object" == typeof exports && "undefined" != typeof module ? module.exports = e(require("@popperjs/core")) : "function" == typeof define && define.amd ? define(["@popperjs/core"], e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).bootstrap = e(t.Popper) }(this, (function (t) { "use strict"; function e(t) { const e = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } }); if (t) for (const i in t) if ("default" !== i) { const s = Object.getOwnPropertyDescriptor(t, i); Object.defineProperty(e, i, s.get ? s : { enumerable: !0, get: () => t[i] }) } return e.default = t, Object.freeze(e) } const i = e(t), s = new Map, n = { set(t, e, i) { s.has(t) || s.set(t, new Map); const n = s.get(t); n.has(e) || 0 === n.size ? n.set(e, i) : console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`) }, get: (t, e) => s.has(t) && s.get(t).get(e) || null, remove(t, e) { if (!s.has(t)) return; const i = s.get(t); i.delete(e), 0 === i.size && s.delete(t) } }, o = "transitionend", r = t => (t && window.CSS && window.CSS.escape && (t = t.replace(/#([^\s"#']+)/g, ((t, e) => `#${CSS.escape(e)}`))), t), a = t => { t.dispatchEvent(new Event(o)) }, l = t => !(!t || "object" != typeof t) && (void 0 !== t.jquery && (t = t[0]), void 0 !== t.nodeType), c = t => l(t) ? t.jquery ? t[0] : t : "string" == typeof t && t.length > 0 ? document.querySelector(r(t)) : null, h = t => { if (!l(t) || 0 === t.getClientRects().length) return !1; const e = "visible" === getComputedStyle(t).getPropertyValue("visibility"), i = t.closest("details:not([open])"); if (!i) return e; if (i !== t) { const e = t.closest("summary"); if (e && e.parentNode !== i) return !1; if (null === e) return !1 } return e }, d = t => !t || t.nodeType !== Node.ELEMENT_NODE || !!t.classList.contains("disabled") || (void 0 !== t.disabled ? t.disabled : t.hasAttribute("disabled") && "false" !== t.getAttribute("disabled")), u = t => { if (!document.documentElement.attachShadow) return null; if ("function" == typeof t.getRootNode) { const e = t.getRootNode(); return e instanceof ShadowRoot ? e : null } return t instanceof ShadowRoot ? t : t.parentNode ? u(t.parentNode) : null }, _ = () => { }, g = t => { t.offsetHeight }, f = () => window.jQuery && !document.body.hasAttribute("data-bs-no-jquery") ? window.jQuery : null, m = [], p = () => "rtl" === document.documentElement.dir, b = t => { var e; e = () => { const e = f(); if (e) { const i = t.NAME, s = e.fn[i]; e.fn[i] = t.jQueryInterface, e.fn[i].Constructor = t, e.fn[i].noConflict = () => (e.fn[i] = s, t.jQueryInterface) } }, "loading" === document.readyState ? (m.length || document.addEventListener("DOMContentLoaded", (() => { for (const t of m) t() })), m.push(e)) : e() }, v = (t, e = [], i = t) => "function" == typeof t ? t(...e) : i, y = (t, e, i = !0) => { if (!i) return void v(t); const s = (t => { if (!t) return 0; let { transitionDuration: e, transitionDelay: i } = window.getComputedStyle(t); const s = Number.parseFloat(e), n = Number.parseFloat(i); return s || n ? (e = e.split(",")[0], i = i.split(",")[0], 1e3 * (Number.parseFloat(e) + Number.parseFloat(i))) : 0 })(e) + 5; let n = !1; const r = ({ target: i }) => { i === e && (n = !0, e.removeEventListener(o, r), v(t)) }; e.addEventListener(o, r), setTimeout((() => { n || a(e) }), s) }, w = (t, e, i, s) => { const n = t.length; let o = t.indexOf(e); return -1 === o ? !i && s ? t[n - 1] : t[0] : (o += i ? 1 : -1, s && (o = (o + n) % n), t[Math.max(0, Math.min(o, n - 1))]) }, A = /[^.]*(?=\..*)\.|.*/, E = /\..*/, C = /::\d+$/, T = {}; let k = 1; const $ = { mouseenter: "mouseover", mouseleave: "mouseout" }, S = new Set(["click", "dblclick", "mouseup", "mousedown", "contextmenu", "mousewheel", "DOMMouseScroll", "mouseover", "mouseout", "mousemove", "selectstart", "selectend", "keydown", "keypress", "keyup", "orientationchange", "touchstart", "touchmove", "touchend", "touchcancel", "pointerdown", "pointermove", "pointerup", "pointerleave", "pointercancel", "gesturestart", "gesturechange", "gestureend", "focus", "blur", "change", "reset", "select", "submit", "focusin", "focusout", "load", "unload", "beforeunload", "resize", "move", "DOMContentLoaded", "readystatechange", "error", "abort", "scroll"]); function L(t, e) { return e && `${e}::${k++}` || t.uidEvent || k++ } function O(t) { const e = L(t); return t.uidEvent = e, T[e] = T[e] || {}, T[e] } function I(t, e, i = null) { return Object.values(t).find((t => t.callable === e && t.delegationSelector === i)) } function D(t, e, i) { const s = "string" == typeof e, n = s ? i : e || i; let o = M(t); return S.has(o) || (o = t), [s, n, o] } function N(t, e, i, s, n) { if ("string" != typeof e || !t) return; let [o, r, a] = D(e, i, s); if (e in $) { const t = t => function (e) { if (!e.relatedTarget || e.relatedTarget !== e.delegateTarget && !e.delegateTarget.contains(e.relatedTarget)) return t.call(this, e) }; r = t(r) } const l = O(t), c = l[a] || (l[a] = {}), h = I(c, r, o ? i : null); if (h) return void (h.oneOff = h.oneOff && n); const d = L(r, e.replace(A, "")), u = o ? function (t, e, i) { return function s(n) { const o = t.querySelectorAll(e); for (let { target: r } = n; r && r !== this; r = r.parentNode)for (const a of o) if (a === r) return F(n, { delegateTarget: r }), s.oneOff && j.off(t, n.type, e, i), i.apply(r, [n]) } }(t, i, r) : function (t, e) { return function i(s) { return F(s, { delegateTarget: t }), i.oneOff && j.off(t, s.type, e), e.apply(t, [s]) } }(t, r); u.delegationSelector = o ? i : null, u.callable = r, u.oneOff = n, u.uidEvent = d, c[d] = u, t.addEventListener(a, u, o) } function P(t, e, i, s, n) { const o = I(e[i], s, n); o && (t.removeEventListener(i, o, Boolean(n)), delete e[i][o.uidEvent]) } function x(t, e, i, s) { const n = e[i] || {}; for (const [o, r] of Object.entries(n)) o.includes(s) && P(t, e, i, r.callable, r.delegationSelector) } function M(t) { return t = t.replace(E, ""), $[t] || t } const j = { on(t, e, i, s) { N(t, e, i, s, !1) }, one(t, e, i, s) { N(t, e, i, s, !0) }, off(t, e, i, s) { if ("string" != typeof e || !t) return; const [n, o, r] = D(e, i, s), a = r !== e, l = O(t), c = l[r] || {}, h = e.startsWith("."); if (void 0 === o) { if (h) for (const i of Object.keys(l)) x(t, l, i, e.slice(1)); for (const [i, s] of Object.entries(c)) { const n = i.replace(C, ""); a && !e.includes(n) || P(t, l, r, s.callable, s.delegationSelector) } } else { if (!Object.keys(c).length) return; P(t, l, r, o, n ? i : null) } }, trigger(t, e, i) { if ("string" != typeof e || !t) return null; const s = f(); let n = null, o = !0, r = !0, a = !1; e !== M(e) && s && (n = s.Event(e, i), s(t).trigger(n), o = !n.isPropagationStopped(), r = !n.isImmediatePropagationStopped(), a = n.isDefaultPrevented()); const l = F(new Event(e, { bubbles: o, cancelable: !0 }), i); return a && l.preventDefault(), r && t.dispatchEvent(l), l.defaultPrevented && n && n.preventDefault(), l } }; function F(t, e = {}) { for (const [i, s] of Object.entries(e)) try { t[i] = s } catch (e) { Object.defineProperty(t, i, { configurable: !0, get: () => s }) } return t } function z(t) { if ("true" === t) return !0; if ("false" === t) return !1; if (t === Number(t).toString()) return Number(t); if ("" === t || "null" === t) return null; if ("string" != typeof t) return t; try { return JSON.parse(decodeURIComponent(t)) } catch (e) { return t } } function H(t) { return t.replace(/[A-Z]/g, (t => `-${t.toLowerCase()}`)) } const B = { setDataAttribute(t, e, i) { t.setAttribute(`data-bs-${H(e)}`, i) }, removeDataAttribute(t, e) { t.removeAttribute(`data-bs-${H(e)}`) }, getDataAttributes(t) { if (!t) return {}; const e = {}, i = Object.keys(t.dataset).filter((t => t.startsWith("bs") && !t.startsWith("bsConfig"))); for (const s of i) { let i = s.replace(/^bs/, ""); i = i.charAt(0).toLowerCase() + i.slice(1, i.length), e[i] = z(t.dataset[s]) } return e }, getDataAttribute: (t, e) => z(t.getAttribute(`data-bs-${H(e)}`)) }; class q { static get Default() { return {} } static get DefaultType() { return {} } static get NAME() { throw new Error('You have to implement the static method "NAME", for each component!') } _getConfig(t) { return t = this._mergeConfigObj(t), t = this._configAfterMerge(t), this._typeCheckConfig(t), t } _configAfterMerge(t) { return t } _mergeConfigObj(t, e) { const i = l(e) ? B.getDataAttribute(e, "config") : {}; return { ...this.constructor.Default, ..."object" == typeof i ? i : {}, ...l(e) ? B.getDataAttributes(e) : {}, ..."object" == typeof t ? t : {} } } _typeCheckConfig(t, e = this.constructor.DefaultType) { for (const [s, n] of Object.entries(e)) { const e = t[s], o = l(e) ? "element" : null == (i = e) ? `${i}` : Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase(); if (!new RegExp(n).test(o)) throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${s}" provided type "${o}" but expected type "${n}".`) } var i } } class W extends q { constructor(t, e) { super(), (t = c(t)) && (this._element = t, this._config = this._getConfig(e), n.set(this._element, this.constructor.DATA_KEY, this)) } dispose() { n.remove(this._element, this.constructor.DATA_KEY), j.off(this._element, this.constructor.EVENT_KEY); for (const t of Object.getOwnPropertyNames(this)) this[t] = null } _queueCallback(t, e, i = !0) { y(t, e, i) } _getConfig(t) { return t = this._mergeConfigObj(t, this._element), t = this._configAfterMerge(t), this._typeCheckConfig(t), t } static getInstance(t) { return n.get(c(t), this.DATA_KEY) } static getOrCreateInstance(t, e = {}) { return this.getInstance(t) || new this(t, "object" == typeof e ? e : null) } static get VERSION() { return "5.3.2" } static get DATA_KEY() { return `bs.${this.NAME}` } static get EVENT_KEY() { return `.${this.DATA_KEY}` } static eventName(t) { return `${t}${this.EVENT_KEY}` } } const R = t => { let e = t.getAttribute("data-bs-target"); if (!e || "#" === e) { let i = t.getAttribute("href"); if (!i || !i.includes("#") && !i.startsWith(".")) return null; i.includes("#") && !i.startsWith("#") && (i = `#${i.split("#")[1]}`), e = i && "#" !== i ? r(i.trim()) : null } return e }, K = { find: (t, e = document.documentElement) => [].concat(...Element.prototype.querySelectorAll.call(e, t)), findOne: (t, e = document.documentElement) => Element.prototype.querySelector.call(e, t), children: (t, e) => [].concat(...t.children).filter((t => t.matches(e))), parents(t, e) { const i = []; let s = t.parentNode.closest(e); for (; s;)i.push(s), s = s.parentNode.closest(e); return i }, prev(t, e) { let i = t.previousElementSibling; for (; i;) { if (i.matches(e)) return [i]; i = i.previousElementSibling } return [] }, next(t, e) { let i = t.nextElementSibling; for (; i;) { if (i.matches(e)) return [i]; i = i.nextElementSibling } return [] }, focusableChildren(t) { const e = ["a", "button", "input", "textarea", "select", "details", "[tabindex]", '[contenteditable="true"]'].map((t => `${t}:not([tabindex^="-"])`)).join(","); return this.find(e, t).filter((t => !d(t) && h(t))) }, getSelectorFromElement(t) { const e = R(t); return e && K.findOne(e) ? e : null }, getElementFromSelector(t) { const e = R(t); return e ? K.findOne(e) : null }, getMultipleElementsFromSelector(t) { const e = R(t); return e ? K.find(e) : [] } }, V = (t, e = "hide") => { const i = `click.dismiss${t.EVENT_KEY}`, s = t.NAME; j.on(document, i, `[data-bs-dismiss="${s}"]`, (function (i) { if (["A", "AREA"].includes(this.tagName) && i.preventDefault(), d(this)) return; const n = K.getElementFromSelector(this) || this.closest(`.${s}`); t.getOrCreateInstance(n)[e]() })) }, Q = ".bs.alert", X = `close${Q}`, Y = `closed${Q}`; class U extends W { static get NAME() { return "alert" } close() { if (j.trigger(this._element, X).defaultPrevented) return; this._element.classList.remove("show"); const t = this._element.classList.contains("fade"); this._queueCallback((() => this._destroyElement()), this._element, t) } _destroyElement() { this._element.remove(), j.trigger(this._element, Y), this.dispose() } static jQueryInterface(t) { return this.each((function () { const e = U.getOrCreateInstance(this); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](this) } })) } } V(U, "close"), b(U); const G = '[data-bs-toggle="button"]'; class J extends W { static get NAME() { return "button" } toggle() { this._element.setAttribute("aria-pressed", this._element.classList.toggle("active")) } static jQueryInterface(t) { return this.each((function () { const e = J.getOrCreateInstance(this); "toggle" === t && e[t]() })) } } j.on(document, "click.bs.button.data-api", G, (t => { t.preventDefault(); const e = t.target.closest(G); J.getOrCreateInstance(e).toggle() })), b(J); const Z = ".bs.swipe", tt = `touchstart${Z}`, et = `touchmove${Z}`, it = `touchend${Z}`, st = `pointerdown${Z}`, nt = `pointerup${Z}`, ot = { endCallback: null, leftCallback: null, rightCallback: null }, rt = { endCallback: "(function|null)", leftCallback: "(function|null)", rightCallback: "(function|null)" }; class at extends q { constructor(t, e) { super(), this._element = t, t && at.isSupported() && (this._config = this._getConfig(e), this._deltaX = 0, this._supportPointerEvents = Boolean(window.PointerEvent), this._initEvents()) } static get Default() { return ot } static get DefaultType() { return rt } static get NAME() { return "swipe" } dispose() { j.off(this._element, Z) } _start(t) { this._supportPointerEvents ? this._eventIsPointerPenTouch(t) && (this._deltaX = t.clientX) : this._deltaX = t.touches[0].clientX } _end(t) { this._eventIsPointerPenTouch(t) && (this._deltaX = t.clientX - this._deltaX), this._handleSwipe(), v(this._config.endCallback) } _move(t) { this._deltaX = t.touches && t.touches.length > 1 ? 0 : t.touches[0].clientX - this._deltaX } _handleSwipe() { const t = Math.abs(this._deltaX); if (t <= 40) return; const e = t / this._deltaX; this._deltaX = 0, e && v(e > 0 ? this._config.rightCallback : this._config.leftCallback) } _initEvents() { this._supportPointerEvents ? (j.on(this._element, st, (t => this._start(t))), j.on(this._element, nt, (t => this._end(t))), this._element.classList.add("pointer-event")) : (j.on(this._element, tt, (t => this._start(t))), j.on(this._element, et, (t => this._move(t))), j.on(this._element, it, (t => this._end(t)))) } _eventIsPointerPenTouch(t) { return this._supportPointerEvents && ("pen" === t.pointerType || "touch" === t.pointerType) } static isSupported() { return "ontouchstart" in document.documentElement || navigator.maxTouchPoints > 0 } } const lt = ".bs.carousel", ct = ".data-api", ht = "next", dt = "prev", ut = "left", _t = "right", gt = `slide${lt}`, ft = `slid${lt}`, mt = `keydown${lt}`, pt = `mouseenter${lt}`, bt = `mouseleave${lt}`, vt = `dragstart${lt}`, yt = `load${lt}${ct}`, wt = `click${lt}${ct}`, At = "carousel", Et = "active", Ct = ".active", Tt = ".carousel-item", kt = Ct + Tt, $t = { ArrowLeft: _t, ArrowRight: ut }, St = { interval: 5e3, keyboard: !0, pause: "hover", ride: !1, touch: !0, wrap: !0 }, Lt = { interval: "(number|boolean)", keyboard: "boolean", pause: "(string|boolean)", ride: "(boolean|string)", touch: "boolean", wrap: "boolean" }; class Ot extends W { constructor(t, e) { super(t, e), this._interval = null, this._activeElement = null, this._isSliding = !1, this.touchTimeout = null, this._swipeHelper = null, this._indicatorsElement = K.findOne(".carousel-indicators", this._element), this._addEventListeners(), this._config.ride === At && this.cycle() } static get Default() { return St } static get DefaultType() { return Lt } static get NAME() { return "carousel" } next() { this._slide(ht) } nextWhenVisible() { !document.hidden && h(this._element) && this.next() } prev() { this._slide(dt) } pause() { this._isSliding && a(this._element), this._clearInterval() } cycle() { this._clearInterval(), this._updateInterval(), this._interval = setInterval((() => this.nextWhenVisible()), this._config.interval) } _maybeEnableCycle() { this._config.ride && (this._isSliding ? j.one(this._element, ft, (() => this.cycle())) : this.cycle()) } to(t) { const e = this._getItems(); if (t > e.length - 1 || t < 0) return; if (this._isSliding) return void j.one(this._element, ft, (() => this.to(t))); const i = this._getItemIndex(this._getActive()); if (i === t) return; const s = t > i ? ht : dt; this._slide(s, e[t]) } dispose() { this._swipeHelper && this._swipeHelper.dispose(), super.dispose() } _configAfterMerge(t) { return t.defaultInterval = t.interval, t } _addEventListeners() { this._config.keyboard && j.on(this._element, mt, (t => this._keydown(t))), "hover" === this._config.pause && (j.on(this._element, pt, (() => this.pause())), j.on(this._element, bt, (() => this._maybeEnableCycle()))), this._config.touch && at.isSupported() && this._addTouchEventListeners() } _addTouchEventListeners() { for (const t of K.find(".carousel-item img", this._element)) j.on(t, vt, (t => t.preventDefault())); const t = { leftCallback: () => this._slide(this._directionToOrder(ut)), rightCallback: () => this._slide(this._directionToOrder(_t)), endCallback: () => { "hover" === this._config.pause && (this.pause(), this.touchTimeout && clearTimeout(this.touchTimeout), this.touchTimeout = setTimeout((() => this._maybeEnableCycle()), 500 + this._config.interval)) } }; this._swipeHelper = new at(this._element, t) } _keydown(t) { if (/input|textarea/i.test(t.target.tagName)) return; const e = $t[t.key]; e && (t.preventDefault(), this._slide(this._directionToOrder(e))) } _getItemIndex(t) { return this._getItems().indexOf(t) } _setActiveIndicatorElement(t) { if (!this._indicatorsElement) return; const e = K.findOne(Ct, this._indicatorsElement); e.classList.remove(Et), e.removeAttribute("aria-current"); const i = K.findOne(`[data-bs-slide-to="${t}"]`, this._indicatorsElement); i && (i.classList.add(Et), i.setAttribute("aria-current", "true")) } _updateInterval() { const t = this._activeElement || this._getActive(); if (!t) return; const e = Number.parseInt(t.getAttribute("data-bs-interval"), 10); this._config.interval = e || this._config.defaultInterval } _slide(t, e = null) { if (this._isSliding) return; const i = this._getActive(), s = t === ht, n = e || w(this._getItems(), i, s, this._config.wrap); if (n === i) return; const o = this._getItemIndex(n), r = e => j.trigger(this._element, e, { relatedTarget: n, direction: this._orderToDirection(t), from: this._getItemIndex(i), to: o }); if (r(gt).defaultPrevented) return; if (!i || !n) return; const a = Boolean(this._interval); this.pause(), this._isSliding = !0, this._setActiveIndicatorElement(o), this._activeElement = n; const l = s ? "carousel-item-start" : "carousel-item-end", c = s ? "carousel-item-next" : "carousel-item-prev"; n.classList.add(c), g(n), i.classList.add(l), n.classList.add(l), this._queueCallback((() => { n.classList.remove(l, c), n.classList.add(Et), i.classList.remove(Et, c, l), this._isSliding = !1, r(ft) }), i, this._isAnimated()), a && this.cycle() } _isAnimated() { return this._element.classList.contains("slide") } _getActive() { return K.findOne(kt, this._element) } _getItems() { return K.find(Tt, this._element) } _clearInterval() { this._interval && (clearInterval(this._interval), this._interval = null) } _directionToOrder(t) { return p() ? t === ut ? dt : ht : t === ut ? ht : dt } _orderToDirection(t) { return p() ? t === dt ? ut : _t : t === dt ? _t : ut } static jQueryInterface(t) { return this.each((function () { const e = Ot.getOrCreateInstance(this, t); if ("number" != typeof t) { if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t]() } } else e.to(t) })) } } j.on(document, wt, "[data-bs-slide], [data-bs-slide-to]", (function (t) { const e = K.getElementFromSelector(this); if (!e || !e.classList.contains(At)) return; t.preventDefault(); const i = Ot.getOrCreateInstance(e), s = this.getAttribute("data-bs-slide-to"); return s ? (i.to(s), void i._maybeEnableCycle()) : "next" === B.getDataAttribute(this, "slide") ? (i.next(), void i._maybeEnableCycle()) : (i.prev(), void i._maybeEnableCycle()) })), j.on(window, yt, (() => { const t = K.find('[data-bs-ride="carousel"]'); for (const e of t) Ot.getOrCreateInstance(e) })), b(Ot); const It = ".bs.collapse", Dt = `show${It}`, Nt = `shown${It}`, Pt = `hide${It}`, xt = `hidden${It}`, Mt = `click${It}.data-api`, jt = "show", Ft = "collapse", zt = "collapsing", Ht = `:scope .${Ft} .${Ft}`, Bt = '[data-bs-toggle="collapse"]', qt = { parent: null, toggle: !0 }, Wt = { parent: "(null|element)", toggle: "boolean" }; class Rt extends W { constructor(t, e) { super(t, e), this._isTransitioning = !1, this._triggerArray = []; const i = K.find(Bt); for (const t of i) { const e = K.getSelectorFromElement(t), i = K.find(e).filter((t => t === this._element)); null !== e && i.length && this._triggerArray.push(t) } this._initializeChildren(), this._config.parent || this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()), this._config.toggle && this.toggle() } static get Default() { return qt } static get DefaultType() { return Wt } static get NAME() { return "collapse" } toggle() { this._isShown() ? this.hide() : this.show() } show() { if (this._isTransitioning || this._isShown()) return; let t = []; if (this._config.parent && (t = this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t => t !== this._element)).map((t => Rt.getOrCreateInstance(t, { toggle: !1 })))), t.length && t[0]._isTransitioning) return; if (j.trigger(this._element, Dt).defaultPrevented) return; for (const e of t) e.hide(); const e = this._getDimension(); this._element.classList.remove(Ft), this._element.classList.add(zt), this._element.style[e] = 0, this._addAriaAndCollapsedClass(this._triggerArray, !0), this._isTransitioning = !0; const i = `scroll${e[0].toUpperCase() + e.slice(1)}`; this._queueCallback((() => { this._isTransitioning = !1, this._element.classList.remove(zt), this._element.classList.add(Ft, jt), this._element.style[e] = "", j.trigger(this._element, Nt) }), this._element, !0), this._element.style[e] = `${this._element[i]}px` } hide() { if (this._isTransitioning || !this._isShown()) return; if (j.trigger(this._element, Pt).defaultPrevented) return; const t = this._getDimension(); this._element.style[t] = `${this._element.getBoundingClientRect()[t]}px`, g(this._element), this._element.classList.add(zt), this._element.classList.remove(Ft, jt); for (const t of this._triggerArray) { const e = K.getElementFromSelector(t); e && !this._isShown(e) && this._addAriaAndCollapsedClass([t], !1) } this._isTransitioning = !0, this._element.style[t] = "", this._queueCallback((() => { this._isTransitioning = !1, this._element.classList.remove(zt), this._element.classList.add(Ft), j.trigger(this._element, xt) }), this._element, !0) } _isShown(t = this._element) { return t.classList.contains(jt) } _configAfterMerge(t) { return t.toggle = Boolean(t.toggle), t.parent = c(t.parent), t } _getDimension() { return this._element.classList.contains("collapse-horizontal") ? "width" : "height" } _initializeChildren() { if (!this._config.parent) return; const t = this._getFirstLevelChildren(Bt); for (const e of t) { const t = K.getElementFromSelector(e); t && this._addAriaAndCollapsedClass([e], this._isShown(t)) } } _getFirstLevelChildren(t) { const e = K.find(Ht, this._config.parent); return K.find(t, this._config.parent).filter((t => !e.includes(t))) } _addAriaAndCollapsedClass(t, e) { if (t.length) for (const i of t) i.classList.toggle("collapsed", !e), i.setAttribute("aria-expanded", e) } static jQueryInterface(t) { const e = {}; return "string" == typeof t && /show|hide/.test(t) && (e.toggle = !1), this.each((function () { const i = Rt.getOrCreateInstance(this, e); if ("string" == typeof t) { if (void 0 === i[t]) throw new TypeError(`No method named "${t}"`); i[t]() } })) } } j.on(document, Mt, Bt, (function (t) { ("A" === t.target.tagName || t.delegateTarget && "A" === t.delegateTarget.tagName) && t.preventDefault(); for (const t of K.getMultipleElementsFromSelector(this)) Rt.getOrCreateInstance(t, { toggle: !1 }).toggle() })), b(Rt); const Kt = "dropdown", Vt = ".bs.dropdown", Qt = ".data-api", Xt = "ArrowUp", Yt = "ArrowDown", Ut = `hide${Vt}`, Gt = `hidden${Vt}`, Jt = `show${Vt}`, Zt = `shown${Vt}`, te = `click${Vt}${Qt}`, ee = `keydown${Vt}${Qt}`, ie = `keyup${Vt}${Qt}`, se = "show", ne = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)', oe = `${ne}.${se}`, re = ".dropdown-menu", ae = p() ? "top-end" : "top-start", le = p() ? "top-start" : "top-end", ce = p() ? "bottom-end" : "bottom-start", he = p() ? "bottom-start" : "bottom-end", de = p() ? "left-start" : "right-start", ue = p() ? "right-start" : "left-start", _e = { autoClose: !0, boundary: "clippingParents", display: "dynamic", offset: [0, 2], popperConfig: null, reference: "toggle" }, ge = { autoClose: "(boolean|string)", boundary: "(string|element)", display: "string", offset: "(array|string|function)", popperConfig: "(null|object|function)", reference: "(string|element|object)" }; class fe extends W { constructor(t, e) { super(t, e), this._popper = null, this._parent = this._element.parentNode, this._menu = K.next(this._element, re)[0] || K.prev(this._element, re)[0] || K.findOne(re, this._parent), this._inNavbar = this._detectNavbar() } static get Default() { return _e } static get DefaultType() { return ge } static get NAME() { return Kt } toggle() { return this._isShown() ? this.hide() : this.show() } show() { if (d(this._element) || this._isShown()) return; const t = { relatedTarget: this._element }; if (!j.trigger(this._element, Jt, t).defaultPrevented) { if (this._createPopper(), "ontouchstart" in document.documentElement && !this._parent.closest(".navbar-nav")) for (const t of [].concat(...document.body.children)) j.on(t, "mouseover", _); this._element.focus(), this._element.setAttribute("aria-expanded", !0), this._menu.classList.add(se), this._element.classList.add(se), j.trigger(this._element, Zt, t) } } hide() { if (d(this._element) || !this._isShown()) return; const t = { relatedTarget: this._element }; this._completeHide(t) } dispose() { this._popper && this._popper.destroy(), super.dispose() } update() { this._inNavbar = this._detectNavbar(), this._popper && this._popper.update() } _completeHide(t) { if (!j.trigger(this._element, Ut, t).defaultPrevented) { if ("ontouchstart" in document.documentElement) for (const t of [].concat(...document.body.children)) j.off(t, "mouseover", _); this._popper && this._popper.destroy(), this._menu.classList.remove(se), this._element.classList.remove(se), this._element.setAttribute("aria-expanded", "false"), B.removeDataAttribute(this._menu, "popper"), j.trigger(this._element, Gt, t) } } _getConfig(t) { if ("object" == typeof (t = super._getConfig(t)).reference && !l(t.reference) && "function" != typeof t.reference.getBoundingClientRect) throw new TypeError(`${Kt.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); return t } _createPopper() { if (void 0 === i) throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)"); let t = this._element; "parent" === this._config.reference ? t = this._parent : l(this._config.reference) ? t = c(this._config.reference) : "object" == typeof this._config.reference && (t = this._config.reference); const e = this._getPopperConfig(); this._popper = i.createPopper(t, this._menu, e) } _isShown() { return this._menu.classList.contains(se) } _getPlacement() { const t = this._parent; if (t.classList.contains("dropend")) return de; if (t.classList.contains("dropstart")) return ue; if (t.classList.contains("dropup-center")) return "top"; if (t.classList.contains("dropdown-center")) return "bottom"; const e = "end" === getComputedStyle(this._menu).getPropertyValue("--bs-position").trim(); return t.classList.contains("dropup") ? e ? le : ae : e ? he : ce } _detectNavbar() { return null !== this._element.closest(".navbar") } _getOffset() { const { offset: t } = this._config; return "string" == typeof t ? t.split(",").map((t => Number.parseInt(t, 10))) : "function" == typeof t ? e => t(e, this._element) : t } _getPopperConfig() { const t = { placement: this._getPlacement(), modifiers: [{ name: "preventOverflow", options: { boundary: this._config.boundary } }, { name: "offset", options: { offset: this._getOffset() } }] }; return (this._inNavbar || "static" === this._config.display) && (B.setDataAttribute(this._menu, "popper", "static"), t.modifiers = [{ name: "applyStyles", enabled: !1 }]), { ...t, ...v(this._config.popperConfig, [t]) } } _selectMenuItem({ key: t, target: e }) { const i = K.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)", this._menu).filter((t => h(t))); i.length && w(i, e, t === Yt, !i.includes(e)).focus() } static jQueryInterface(t) { return this.each((function () { const e = fe.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t]() } })) } static clearMenus(t) { if (2 === t.button || "keyup" === t.type && "Tab" !== t.key) return; const e = K.find(oe); for (const i of e) { const e = fe.getInstance(i); if (!e || !1 === e._config.autoClose) continue; const s = t.composedPath(), n = s.includes(e._menu); if (s.includes(e._element) || "inside" === e._config.autoClose && !n || "outside" === e._config.autoClose && n) continue; if (e._menu.contains(t.target) && ("keyup" === t.type && "Tab" === t.key || /input|select|option|textarea|form/i.test(t.target.tagName))) continue; const o = { relatedTarget: e._element }; "click" === t.type && (o.clickEvent = t), e._completeHide(o) } } static dataApiKeydownHandler(t) { const e = /input|textarea/i.test(t.target.tagName), i = "Escape" === t.key, s = [Xt, Yt].includes(t.key); if (!s && !i) return; if (e && !i) return; t.preventDefault(); const n = this.matches(ne) ? this : K.prev(this, ne)[0] || K.next(this, ne)[0] || K.findOne(ne, t.delegateTarget.parentNode), o = fe.getOrCreateInstance(n); if (s) return t.stopPropagation(), o.show(), void o._selectMenuItem(t); o._isShown() && (t.stopPropagation(), o.hide(), n.focus()) } } j.on(document, ee, ne, fe.dataApiKeydownHandler), j.on(document, ee, re, fe.dataApiKeydownHandler), j.on(document, te, fe.clearMenus), j.on(document, ie, fe.clearMenus), j.on(document, te, ne, (function (t) { t.preventDefault(), fe.getOrCreateInstance(this).toggle() })), b(fe); const me = "backdrop", pe = "show", be = `mousedown.bs.${me}`, ve = { className: "modal-backdrop", clickCallback: null, isAnimated: !1, isVisible: !0, rootElement: "body" }, ye = { className: "string", clickCallback: "(function|null)", isAnimated: "boolean", isVisible: "boolean", rootElement: "(element|string)" }; class we extends q { constructor(t) { super(), this._config = this._getConfig(t), this._isAppended = !1, this._element = null } static get Default() { return ve } static get DefaultType() { return ye } static get NAME() { return me } show(t) { if (!this._config.isVisible) return void v(t); this._append(); const e = this._getElement(); this._config.isAnimated && g(e), e.classList.add(pe), this._emulateAnimation((() => { v(t) })) } hide(t) { this._config.isVisible ? (this._getElement().classList.remove(pe), this._emulateAnimation((() => { this.dispose(), v(t) }))) : v(t) } dispose() { this._isAppended && (j.off(this._element, be), this._element.remove(), this._isAppended = !1) } _getElement() { if (!this._element) { const t = document.createElement("div"); t.className = this._config.className, this._config.isAnimated && t.classList.add("fade"), this._element = t } return this._element } _configAfterMerge(t) { return t.rootElement = c(t.rootElement), t } _append() { if (this._isAppended) return; const t = this._getElement(); this._config.rootElement.append(t), j.on(t, be, (() => { v(this._config.clickCallback) })), this._isAppended = !0 } _emulateAnimation(t) { y(t, this._getElement(), this._config.isAnimated) } } const Ae = ".bs.focustrap", Ee = `focusin${Ae}`, Ce = `keydown.tab${Ae}`, Te = "backward", ke = { autofocus: !0, trapElement: null }, $e = { autofocus: "boolean", trapElement: "element" }; class Se extends q { constructor(t) { super(), this._config = this._getConfig(t), this._isActive = !1, this._lastTabNavDirection = null } static get Default() { return ke } static get DefaultType() { return $e } static get NAME() { return "focustrap" } activate() { this._isActive || (this._config.autofocus && this._config.trapElement.focus(), j.off(document, Ae), j.on(document, Ee, (t => this._handleFocusin(t))), j.on(document, Ce, (t => this._handleKeydown(t))), this._isActive = !0) } deactivate() { this._isActive && (this._isActive = !1, j.off(document, Ae)) } _handleFocusin(t) { const { trapElement: e } = this._config; if (t.target === document || t.target === e || e.contains(t.target)) return; const i = K.focusableChildren(e); 0 === i.length ? e.focus() : this._lastTabNavDirection === Te ? i[i.length - 1].focus() : i[0].focus() } _handleKeydown(t) { "Tab" === t.key && (this._lastTabNavDirection = t.shiftKey ? Te : "forward") } } const Le = ".fixed-top, .fixed-bottom, .is-fixed, .sticky-top", Oe = ".sticky-top", Ie = "padding-right", De = "margin-right"; class Ne { constructor() { this._element = document.body } getWidth() { const t = document.documentElement.clientWidth; return Math.abs(window.innerWidth - t) } hide() { const t = this.getWidth(); this._disableOverFlow(), this._setElementAttributes(this._element, Ie, (e => e + t)), this._setElementAttributes(Le, Ie, (e => e + t)), this._setElementAttributes(Oe, De, (e => e - t)) } reset() { this._resetElementAttributes(this._element, "overflow"), this._resetElementAttributes(this._element, Ie), this._resetElementAttributes(Le, Ie), this._resetElementAttributes(Oe, De) } isOverflowing() { return this.getWidth() > 0 } _disableOverFlow() { this._saveInitialAttribute(this._element, "overflow"), this._element.style.overflow = "hidden" } _setElementAttributes(t, e, i) { const s = this.getWidth(); this._applyManipulationCallback(t, (t => { if (t !== this._element && window.innerWidth > t.clientWidth + s) return; this._saveInitialAttribute(t, e); const n = window.getComputedStyle(t).getPropertyValue(e); t.style.setProperty(e, `${i(Number.parseFloat(n))}px`) })) } _saveInitialAttribute(t, e) { const i = t.style.getPropertyValue(e); i && B.setDataAttribute(t, e, i) } _resetElementAttributes(t, e) { this._applyManipulationCallback(t, (t => { const i = B.getDataAttribute(t, e); null !== i ? (B.removeDataAttribute(t, e), t.style.setProperty(e, i)) : t.style.removeProperty(e) })) } _applyManipulationCallback(t, e) { if (l(t)) e(t); else for (const i of K.find(t, this._element)) e(i) } } const Pe = ".bs.modal", xe = `hide${Pe}`, Me = `hidePrevented${Pe}`, je = `hidden${Pe}`, Fe = `show${Pe}`, ze = `shown${Pe}`, He = `resize${Pe}`, Be = `click.dismiss${Pe}`, qe = `mousedown.dismiss${Pe}`, We = `keydown.dismiss${Pe}`, Re = `click${Pe}.data-api`, Ke = "modal-open", Ve = "show", Qe = "modal-static", Xe = { backdrop: !0, focus: !0, keyboard: !0 }, Ye = { backdrop: "(boolean|string)", focus: "boolean", keyboard: "boolean" }; class Ue extends W { constructor(t, e) { super(t, e), this._dialog = K.findOne(".modal-dialog", this._element), this._backdrop = this._initializeBackDrop(), this._focustrap = this._initializeFocusTrap(), this._isShown = !1, this._isTransitioning = !1, this._scrollBar = new Ne, this._addEventListeners() } static get Default() { return Xe } static get DefaultType() { return Ye } static get NAME() { return "modal" } toggle(t) { return this._isShown ? this.hide() : this.show(t) } show(t) { this._isShown || this._isTransitioning || j.trigger(this._element, Fe, { relatedTarget: t }).defaultPrevented || (this._isShown = !0, this._isTransitioning = !0, this._scrollBar.hide(), document.body.classList.add(Ke), this._adjustDialog(), this._backdrop.show((() => this._showElement(t)))) } hide() { this._isShown && !this._isTransitioning && (j.trigger(this._element, xe).defaultPrevented || (this._isShown = !1, this._isTransitioning = !0, this._focustrap.deactivate(), this._element.classList.remove(Ve), this._queueCallback((() => this._hideModal()), this._element, this._isAnimated()))) } dispose() { j.off(window, Pe), j.off(this._dialog, Pe), this._backdrop.dispose(), this._focustrap.deactivate(), super.dispose() } handleUpdate() { this._adjustDialog() } _initializeBackDrop() { return new we({ isVisible: Boolean(this._config.backdrop), isAnimated: this._isAnimated() }) } _initializeFocusTrap() { return new Se({ trapElement: this._element }) } _showElement(t) { document.body.contains(this._element) || document.body.append(this._element), this._element.style.display = "block", this._element.removeAttribute("aria-hidden"), this._element.setAttribute("aria-modal", !0), this._element.setAttribute("role", "dialog"), this._element.scrollTop = 0; const e = K.findOne(".modal-body", this._dialog); e && (e.scrollTop = 0), g(this._element), this._element.classList.add(Ve), this._queueCallback((() => { this._config.focus && this._focustrap.activate(), this._isTransitioning = !1, j.trigger(this._element, ze, { relatedTarget: t }) }), this._dialog, this._isAnimated()) } _addEventListeners() { j.on(this._element, We, (t => { "Escape" === t.key && (this._config.keyboard ? this.hide() : this._triggerBackdropTransition()) })), j.on(window, He, (() => { this._isShown && !this._isTransitioning && this._adjustDialog() })), j.on(this._element, qe, (t => { j.one(this._element, Be, (e => { this._element === t.target && this._element === e.target && ("static" !== this._config.backdrop ? this._config.backdrop && this.hide() : this._triggerBackdropTransition()) })) })) } _hideModal() { this._element.style.display = "none", this._element.setAttribute("aria-hidden", !0), this._element.removeAttribute("aria-modal"), this._element.removeAttribute("role"), this._isTransitioning = !1, this._backdrop.hide((() => { document.body.classList.remove(Ke), this._resetAdjustments(), this._scrollBar.reset(), j.trigger(this._element, je) })) } _isAnimated() { return this._element.classList.contains("fade") } _triggerBackdropTransition() { if (j.trigger(this._element, Me).defaultPrevented) return; const t = this._element.scrollHeight > document.documentElement.clientHeight, e = this._element.style.overflowY; "hidden" === e || this._element.classList.contains(Qe) || (t || (this._element.style.overflowY = "hidden"), this._element.classList.add(Qe), this._queueCallback((() => { this._element.classList.remove(Qe), this._queueCallback((() => { this._element.style.overflowY = e }), this._dialog) }), this._dialog), this._element.focus()) } _adjustDialog() { const t = this._element.scrollHeight > document.documentElement.clientHeight, e = this._scrollBar.getWidth(), i = e > 0; if (i && !t) { const t = p() ? "paddingLeft" : "paddingRight"; this._element.style[t] = `${e}px` } if (!i && t) { const t = p() ? "paddingRight" : "paddingLeft"; this._element.style[t] = `${e}px` } } _resetAdjustments() { this._element.style.paddingLeft = "", this._element.style.paddingRight = "" } static jQueryInterface(t, e) { return this.each((function () { const i = Ue.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === i[t]) throw new TypeError(`No method named "${t}"`); i[t](e) } })) } } j.on(document, Re, '[data-bs-toggle="modal"]', (function (t) { const e = K.getElementFromSelector(this);["A", "AREA"].includes(this.tagName) && t.preventDefault(), j.one(e, Fe, (t => { t.defaultPrevented || j.one(e, je, (() => { h(this) && this.focus() })) })); const i = K.findOne(".modal.show"); i && Ue.getInstance(i).hide(), Ue.getOrCreateInstance(e).toggle(this) })), V(Ue), b(Ue); const Ge = ".bs.offcanvas", Je = ".data-api", Ze = `load${Ge}${Je}`, ti = "show", ei = "showing", ii = "hiding", si = ".offcanvas.show", ni = `show${Ge}`, oi = `shown${Ge}`, ri = `hide${Ge}`, ai = `hidePrevented${Ge}`, li = `hidden${Ge}`, ci = `resize${Ge}`, hi = `click${Ge}${Je}`, di = `keydown.dismiss${Ge}`, ui = { backdrop: !0, keyboard: !0, scroll: !1 }, _i = { backdrop: "(boolean|string)", keyboard: "boolean", scroll: "boolean" }; class gi extends W { constructor(t, e) { super(t, e), this._isShown = !1, this._backdrop = this._initializeBackDrop(), this._focustrap = this._initializeFocusTrap(), this._addEventListeners() } static get Default() { return ui } static get DefaultType() { return _i } static get NAME() { return "offcanvas" } toggle(t) { return this._isShown ? this.hide() : this.show(t) } show(t) { this._isShown || j.trigger(this._element, ni, { relatedTarget: t }).defaultPrevented || (this._isShown = !0, this._backdrop.show(), this._config.scroll || (new Ne).hide(), this._element.setAttribute("aria-modal", !0), this._element.setAttribute("role", "dialog"), this._element.classList.add(ei), this._queueCallback((() => { this._config.scroll && !this._config.backdrop || this._focustrap.activate(), this._element.classList.add(ti), this._element.classList.remove(ei), j.trigger(this._element, oi, { relatedTarget: t }) }), this._element, !0)) } hide() { this._isShown && (j.trigger(this._element, ri).defaultPrevented || (this._focustrap.deactivate(), this._element.blur(), this._isShown = !1, this._element.classList.add(ii), this._backdrop.hide(), this._queueCallback((() => { this._element.classList.remove(ti, ii), this._element.removeAttribute("aria-modal"), this._element.removeAttribute("role"), this._config.scroll || (new Ne).reset(), j.trigger(this._element, li) }), this._element, !0))) } dispose() { this._backdrop.dispose(), this._focustrap.deactivate(), super.dispose() } _initializeBackDrop() { const t = Boolean(this._config.backdrop); return new we({ className: "offcanvas-backdrop", isVisible: t, isAnimated: !0, rootElement: this._element.parentNode, clickCallback: t ? () => { "static" !== this._config.backdrop ? this.hide() : j.trigger(this._element, ai) } : null }) } _initializeFocusTrap() { return new Se({ trapElement: this._element }) } _addEventListeners() { j.on(this._element, di, (t => { "Escape" === t.key && (this._config.keyboard ? this.hide() : j.trigger(this._element, ai)) })) } static jQueryInterface(t) { return this.each((function () { const e = gi.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t](this) } })) } } j.on(document, hi, '[data-bs-toggle="offcanvas"]', (function (t) { const e = K.getElementFromSelector(this); if (["A", "AREA"].includes(this.tagName) && t.preventDefault(), d(this)) return; j.one(e, li, (() => { h(this) && this.focus() })); const i = K.findOne(si); i && i !== e && gi.getInstance(i).hide(), gi.getOrCreateInstance(e).toggle(this) })), j.on(window, Ze, (() => { for (const t of K.find(si)) gi.getOrCreateInstance(t).show() })), j.on(window, ci, (() => { for (const t of K.find("[aria-modal][class*=show][class*=offcanvas-]")) "fixed" !== getComputedStyle(t).position && gi.getOrCreateInstance(t).hide() })), V(gi), b(gi); const fi = { "*": ["class", "dir", "id", "lang", "role", /^aria-[\w-]*$/i], a: ["target", "href", "title", "rel"], area: [], b: [], br: [], col: [], code: [], div: [], em: [], hr: [], h1: [], h2: [], h3: [], h4: [], h5: [], h6: [], i: [], img: ["src", "srcset", "alt", "title", "width", "height"], li: [], ol: [], p: [], pre: [], s: [], small: [], span: [], sub: [], sup: [], strong: [], u: [], ul: [] }, mi = new Set(["background", "cite", "href", "itemtype", "longdesc", "poster", "src", "xlink:href"]), pi = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i, bi = (t, e) => { const i = t.nodeName.toLowerCase(); return e.includes(i) ? !mi.has(i) || Boolean(pi.test(t.nodeValue)) : e.filter((t => t instanceof RegExp)).some((t => t.test(i))) }, vi = { allowList: fi, content: {}, extraClass: "", html: !1, sanitize: !0, sanitizeFn: null, template: "
                                                                                                                                                                                                                                                                                                          " }, yi = { allowList: "object", content: "object", extraClass: "(string|function)", html: "boolean", sanitize: "boolean", sanitizeFn: "(null|function)", template: "string" }, wi = { entry: "(string|element|function|null)", selector: "(string|element)" }; class Ai extends q { constructor(t) { super(), this._config = this._getConfig(t) } static get Default() { return vi } static get DefaultType() { return yi } static get NAME() { return "TemplateFactory" } getContent() { return Object.values(this._config.content).map((t => this._resolvePossibleFunction(t))).filter(Boolean) } hasContent() { return this.getContent().length > 0 } changeContent(t) { return this._checkContent(t), this._config.content = { ...this._config.content, ...t }, this } toHtml() { const t = document.createElement("div"); t.innerHTML = this._maybeSanitize(this._config.template); for (const [e, i] of Object.entries(this._config.content)) this._setContent(t, i, e); const e = t.children[0], i = this._resolvePossibleFunction(this._config.extraClass); return i && e.classList.add(...i.split(" ")), e } _typeCheckConfig(t) { super._typeCheckConfig(t), this._checkContent(t.content) } _checkContent(t) { for (const [e, i] of Object.entries(t)) super._typeCheckConfig({ selector: e, entry: i }, wi) } _setContent(t, e, i) { const s = K.findOne(i, t); s && ((e = this._resolvePossibleFunction(e)) ? l(e) ? this._putElementInTemplate(c(e), s) : this._config.html ? s.innerHTML = this._maybeSanitize(e) : s.textContent = e : s.remove()) } _maybeSanitize(t) { return this._config.sanitize ? function (t, e, i) { if (!t.length) return t; if (i && "function" == typeof i) return i(t); const s = (new window.DOMParser).parseFromString(t, "text/html"), n = [].concat(...s.body.querySelectorAll("*")); for (const t of n) { const i = t.nodeName.toLowerCase(); if (!Object.keys(e).includes(i)) { t.remove(); continue } const s = [].concat(...t.attributes), n = [].concat(e["*"] || [], e[i] || []); for (const e of s) bi(e, n) || t.removeAttribute(e.nodeName) } return s.body.innerHTML }(t, this._config.allowList, this._config.sanitizeFn) : t } _resolvePossibleFunction(t) { return v(t, [this]) } _putElementInTemplate(t, e) { if (this._config.html) return e.innerHTML = "", void e.append(t); e.textContent = t.textContent } } const Ei = new Set(["sanitize", "allowList", "sanitizeFn"]), Ci = "fade", Ti = "show", ki = ".modal", $i = "hide.bs.modal", Si = "hover", Li = "focus", Oi = { AUTO: "auto", TOP: "top", RIGHT: p() ? "left" : "right", BOTTOM: "bottom", LEFT: p() ? "right" : "left" }, Ii = { allowList: fi, animation: !0, boundary: "clippingParents", container: !1, customClass: "", delay: 0, fallbackPlacements: ["top", "right", "bottom", "left"], html: !1, offset: [0, 6], placement: "top", popperConfig: null, sanitize: !0, sanitizeFn: null, selector: !1, template: '', title: "", trigger: "hover focus" }, Di = { allowList: "object", animation: "boolean", boundary: "(string|element)", container: "(string|element|boolean)", customClass: "(string|function)", delay: "(number|object)", fallbackPlacements: "array", html: "boolean", offset: "(array|string|function)", placement: "(string|function)", popperConfig: "(null|object|function)", sanitize: "boolean", sanitizeFn: "(null|function)", selector: "(string|boolean)", template: "string", title: "(string|element|function)", trigger: "string" }; class Ni extends W { constructor(t, e) { if (void 0 === i) throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)"); super(t, e), this._isEnabled = !0, this._timeout = 0, this._isHovered = null, this._activeTrigger = {}, this._popper = null, this._templateFactory = null, this._newContent = null, this.tip = null, this._setListeners(), this._config.selector || this._fixTitle() } static get Default() { return Ii } static get DefaultType() { return Di } static get NAME() { return "tooltip" } enable() { this._isEnabled = !0 } disable() { this._isEnabled = !1 } toggleEnabled() { this._isEnabled = !this._isEnabled } toggle() { this._isEnabled && (this._activeTrigger.click = !this._activeTrigger.click, this._isShown() ? this._leave() : this._enter()) } dispose() { clearTimeout(this._timeout), j.off(this._element.closest(ki), $i, this._hideModalHandler), this._element.getAttribute("data-bs-original-title") && this._element.setAttribute("title", this._element.getAttribute("data-bs-original-title")), this._disposePopper(), super.dispose() } show() { if ("none" === this._element.style.display) throw new Error("Please use show on visible elements"); if (!this._isWithContent() || !this._isEnabled) return; const t = j.trigger(this._element, this.constructor.eventName("show")), e = (u(this._element) || this._element.ownerDocument.documentElement).contains(this._element); if (t.defaultPrevented || !e) return; this._disposePopper(); const i = this._getTipElement(); this._element.setAttribute("aria-describedby", i.getAttribute("id")); const { container: s } = this._config; if (this._element.ownerDocument.documentElement.contains(this.tip) || (s.append(i), j.trigger(this._element, this.constructor.eventName("inserted"))), this._popper = this._createPopper(i), i.classList.add(Ti), "ontouchstart" in document.documentElement) for (const t of [].concat(...document.body.children)) j.on(t, "mouseover", _); this._queueCallback((() => { j.trigger(this._element, this.constructor.eventName("shown")), !1 === this._isHovered && this._leave(), this._isHovered = !1 }), this.tip, this._isAnimated()) } hide() { if (this._isShown() && !j.trigger(this._element, this.constructor.eventName("hide")).defaultPrevented) { if (this._getTipElement().classList.remove(Ti), "ontouchstart" in document.documentElement) for (const t of [].concat(...document.body.children)) j.off(t, "mouseover", _); this._activeTrigger.click = !1, this._activeTrigger[Li] = !1, this._activeTrigger[Si] = !1, this._isHovered = null, this._queueCallback((() => { this._isWithActiveTrigger() || (this._isHovered || this._disposePopper(), this._element.removeAttribute("aria-describedby"), j.trigger(this._element, this.constructor.eventName("hidden"))) }), this.tip, this._isAnimated()) } } update() { this._popper && this._popper.update() } _isWithContent() { return Boolean(this._getTitle()) } _getTipElement() { return this.tip || (this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())), this.tip } _createTipElement(t) { const e = this._getTemplateFactory(t).toHtml(); if (!e) return null; e.classList.remove(Ci, Ti), e.classList.add(`bs-${this.constructor.NAME}-auto`); const i = (t => { do { t += Math.floor(1e6 * Math.random()) } while (document.getElementById(t)); return t })(this.constructor.NAME).toString(); return e.setAttribute("id", i), this._isAnimated() && e.classList.add(Ci), e } setContent(t) { this._newContent = t, this._isShown() && (this._disposePopper(), this.show()) } _getTemplateFactory(t) { return this._templateFactory ? this._templateFactory.changeContent(t) : this._templateFactory = new Ai({ ...this._config, content: t, extraClass: this._resolvePossibleFunction(this._config.customClass) }), this._templateFactory } _getContentForTemplate() { return { ".tooltip-inner": this._getTitle() } } _getTitle() { return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute("data-bs-original-title") } _initializeOnDelegatedTarget(t) { return this.constructor.getOrCreateInstance(t.delegateTarget, this._getDelegateConfig()) } _isAnimated() { return this._config.animation || this.tip && this.tip.classList.contains(Ci) } _isShown() { return this.tip && this.tip.classList.contains(Ti) } _createPopper(t) { const e = v(this._config.placement, [this, t, this._element]), s = Oi[e.toUpperCase()]; return i.createPopper(this._element, t, this._getPopperConfig(s)) } _getOffset() { const { offset: t } = this._config; return "string" == typeof t ? t.split(",").map((t => Number.parseInt(t, 10))) : "function" == typeof t ? e => t(e, this._element) : t } _resolvePossibleFunction(t) { return v(t, [this._element]) } _getPopperConfig(t) { const e = { placement: t, modifiers: [{ name: "flip", options: { fallbackPlacements: this._config.fallbackPlacements } }, { name: "offset", options: { offset: this._getOffset() } }, { name: "preventOverflow", options: { boundary: this._config.boundary } }, { name: "arrow", options: { element: `.${this.constructor.NAME}-arrow` } }, { name: "preSetPlacement", enabled: !0, phase: "beforeMain", fn: t => { this._getTipElement().setAttribute("data-popper-placement", t.state.placement) } }] }; return { ...e, ...v(this._config.popperConfig, [e]) } } _setListeners() { const t = this._config.trigger.split(" "); for (const e of t) if ("click" === e) j.on(this._element, this.constructor.eventName("click"), this._config.selector, (t => { this._initializeOnDelegatedTarget(t).toggle() })); else if ("manual" !== e) { const t = e === Si ? this.constructor.eventName("mouseenter") : this.constructor.eventName("focusin"), i = e === Si ? this.constructor.eventName("mouseleave") : this.constructor.eventName("focusout"); j.on(this._element, t, this._config.selector, (t => { const e = this._initializeOnDelegatedTarget(t); e._activeTrigger["focusin" === t.type ? Li : Si] = !0, e._enter() })), j.on(this._element, i, this._config.selector, (t => { const e = this._initializeOnDelegatedTarget(t); e._activeTrigger["focusout" === t.type ? Li : Si] = e._element.contains(t.relatedTarget), e._leave() })) } this._hideModalHandler = () => { this._element && this.hide() }, j.on(this._element.closest(ki), $i, this._hideModalHandler) } _fixTitle() { const t = this._element.getAttribute("title"); t && (this._element.getAttribute("aria-label") || this._element.textContent.trim() || this._element.setAttribute("aria-label", t), this._element.setAttribute("data-bs-original-title", t), this._element.removeAttribute("title")) } _enter() { this._isShown() || this._isHovered ? this._isHovered = !0 : (this._isHovered = !0, this._setTimeout((() => { this._isHovered && this.show() }), this._config.delay.show)) } _leave() { this._isWithActiveTrigger() || (this._isHovered = !1, this._setTimeout((() => { this._isHovered || this.hide() }), this._config.delay.hide)) } _setTimeout(t, e) { clearTimeout(this._timeout), this._timeout = setTimeout(t, e) } _isWithActiveTrigger() { return Object.values(this._activeTrigger).includes(!0) } _getConfig(t) { const e = B.getDataAttributes(this._element); for (const t of Object.keys(e)) Ei.has(t) && delete e[t]; return t = { ...e, ..."object" == typeof t && t ? t : {} }, t = this._mergeConfigObj(t), t = this._configAfterMerge(t), this._typeCheckConfig(t), t } _configAfterMerge(t) { return t.container = !1 === t.container ? document.body : c(t.container), "number" == typeof t.delay && (t.delay = { show: t.delay, hide: t.delay }), "number" == typeof t.title && (t.title = t.title.toString()), "number" == typeof t.content && (t.content = t.content.toString()), t } _getDelegateConfig() { const t = {}; for (const [e, i] of Object.entries(this._config)) this.constructor.Default[e] !== i && (t[e] = i); return t.selector = !1, t.trigger = "manual", t } _disposePopper() { this._popper && (this._popper.destroy(), this._popper = null), this.tip && (this.tip.remove(), this.tip = null) } static jQueryInterface(t) { return this.each((function () { const e = Ni.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t]() } })) } } b(Ni); const Pi = { ...Ni.Default, content: "", offset: [0, 8], placement: "right", template: '', trigger: "click" }, xi = { ...Ni.DefaultType, content: "(null|string|element|function)" }; class Mi extends Ni { static get Default() { return Pi } static get DefaultType() { return xi } static get NAME() { return "popover" } _isWithContent() { return this._getTitle() || this._getContent() } _getContentForTemplate() { return { ".popover-header": this._getTitle(), ".popover-body": this._getContent() } } _getContent() { return this._resolvePossibleFunction(this._config.content) } static jQueryInterface(t) { return this.each((function () { const e = Mi.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t]() } })) } } b(Mi); const ji = ".bs.scrollspy", Fi = `activate${ji}`, zi = `click${ji}`, Hi = `load${ji}.data-api`, Bi = "active", qi = "[href]", Wi = ".nav-link", Ri = `${Wi}, .nav-item > ${Wi}, .list-group-item`, Ki = { offset: null, rootMargin: "0px 0px -25%", smoothScroll: !1, target: null, threshold: [.1, .5, 1] }, Vi = { offset: "(number|null)", rootMargin: "string", smoothScroll: "boolean", target: "element", threshold: "array" }; class Qi extends W { constructor(t, e) { super(t, e), this._targetLinks = new Map, this._observableSections = new Map, this._rootElement = "visible" === getComputedStyle(this._element).overflowY ? null : this._element, this._activeTarget = null, this._observer = null, this._previousScrollData = { visibleEntryTop: 0, parentScrollTop: 0 }, this.refresh() } static get Default() { return Ki } static get DefaultType() { return Vi } static get NAME() { return "scrollspy" } refresh() { this._initializeTargetsAndObservables(), this._maybeEnableSmoothScroll(), this._observer ? this._observer.disconnect() : this._observer = this._getNewObserver(); for (const t of this._observableSections.values()) this._observer.observe(t) } dispose() { this._observer.disconnect(), super.dispose() } _configAfterMerge(t) { return t.target = c(t.target) || document.body, t.rootMargin = t.offset ? `${t.offset}px 0px -30%` : t.rootMargin, "string" == typeof t.threshold && (t.threshold = t.threshold.split(",").map((t => Number.parseFloat(t)))), t } _maybeEnableSmoothScroll() { this._config.smoothScroll && (j.off(this._config.target, zi), j.on(this._config.target, zi, qi, (t => { const e = this._observableSections.get(t.target.hash); if (e) { t.preventDefault(); const i = this._rootElement || window, s = e.offsetTop - this._element.offsetTop; if (i.scrollTo) return void i.scrollTo({ top: s, behavior: "smooth" }); i.scrollTop = s } }))) } _getNewObserver() { const t = { root: this._rootElement, threshold: this._config.threshold, rootMargin: this._config.rootMargin }; return new IntersectionObserver((t => this._observerCallback(t)), t) } _observerCallback(t) { const e = t => this._targetLinks.get(`#${t.target.id}`), i = t => { this._previousScrollData.visibleEntryTop = t.target.offsetTop, this._process(e(t)) }, s = (this._rootElement || document.documentElement).scrollTop, n = s >= this._previousScrollData.parentScrollTop; this._previousScrollData.parentScrollTop = s; for (const o of t) { if (!o.isIntersecting) { this._activeTarget = null, this._clearActiveClass(e(o)); continue } const t = o.target.offsetTop >= this._previousScrollData.visibleEntryTop; if (n && t) { if (i(o), !s) return } else n || t || i(o) } } _initializeTargetsAndObservables() { this._targetLinks = new Map, this._observableSections = new Map; const t = K.find(qi, this._config.target); for (const e of t) { if (!e.hash || d(e)) continue; const t = K.findOne(decodeURI(e.hash), this._element); h(t) && (this._targetLinks.set(decodeURI(e.hash), e), this._observableSections.set(e.hash, t)) } } _process(t) { this._activeTarget !== t && (this._clearActiveClass(this._config.target), this._activeTarget = t, t.classList.add(Bi), this._activateParents(t), j.trigger(this._element, Fi, { relatedTarget: t })) } _activateParents(t) { if (t.classList.contains("dropdown-item")) K.findOne(".dropdown-toggle", t.closest(".dropdown")).classList.add(Bi); else for (const e of K.parents(t, ".nav, .list-group")) for (const t of K.prev(e, Ri)) t.classList.add(Bi) } _clearActiveClass(t) { t.classList.remove(Bi); const e = K.find(`${qi}.${Bi}`, t); for (const t of e) t.classList.remove(Bi) } static jQueryInterface(t) { return this.each((function () { const e = Qi.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t]() } })) } } j.on(window, Hi, (() => { for (const t of K.find('[data-bs-spy="scroll"]')) Qi.getOrCreateInstance(t) })), b(Qi); const Xi = ".bs.tab", Yi = `hide${Xi}`, Ui = `hidden${Xi}`, Gi = `show${Xi}`, Ji = `shown${Xi}`, Zi = `click${Xi}`, ts = `keydown${Xi}`, es = `load${Xi}`, is = "ArrowLeft", ss = "ArrowRight", ns = "ArrowUp", os = "ArrowDown", rs = "Home", as = "End", ls = "active", cs = "fade", hs = "show", ds = ".dropdown-toggle", us = `:not(${ds})`, _s = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]', gs = `.nav-link${us}, .list-group-item${us}, [role="tab"]${us}, ${_s}`, fs = `.${ls}[data-bs-toggle="tab"], .${ls}[data-bs-toggle="pill"], .${ls}[data-bs-toggle="list"]`; class ms extends W { constructor(t) { super(t), this._parent = this._element.closest('.list-group, .nav, [role="tablist"]'), this._parent && (this._setInitialAttributes(this._parent, this._getChildren()), j.on(this._element, ts, (t => this._keydown(t)))) } static get NAME() { return "tab" } show() { const t = this._element; if (this._elemIsActive(t)) return; const e = this._getActiveElem(), i = e ? j.trigger(e, Yi, { relatedTarget: t }) : null; j.trigger(t, Gi, { relatedTarget: e }).defaultPrevented || i && i.defaultPrevented || (this._deactivate(e, t), this._activate(t, e)) } _activate(t, e) { t && (t.classList.add(ls), this._activate(K.getElementFromSelector(t)), this._queueCallback((() => { "tab" === t.getAttribute("role") ? (t.removeAttribute("tabindex"), t.setAttribute("aria-selected", !0), this._toggleDropDown(t, !0), j.trigger(t, Ji, { relatedTarget: e })) : t.classList.add(hs) }), t, t.classList.contains(cs))) } _deactivate(t, e) { t && (t.classList.remove(ls), t.blur(), this._deactivate(K.getElementFromSelector(t)), this._queueCallback((() => { "tab" === t.getAttribute("role") ? (t.setAttribute("aria-selected", !1), t.setAttribute("tabindex", "-1"), this._toggleDropDown(t, !1), j.trigger(t, Ui, { relatedTarget: e })) : t.classList.remove(hs) }), t, t.classList.contains(cs))) } _keydown(t) { if (![is, ss, ns, os, rs, as].includes(t.key)) return; t.stopPropagation(), t.preventDefault(); const e = this._getChildren().filter((t => !d(t))); let i; if ([rs, as].includes(t.key)) i = e[t.key === rs ? 0 : e.length - 1]; else { const s = [ss, os].includes(t.key); i = w(e, t.target, s, !0) } i && (i.focus({ preventScroll: !0 }), ms.getOrCreateInstance(i).show()) } _getChildren() { return K.find(gs, this._parent) } _getActiveElem() { return this._getChildren().find((t => this._elemIsActive(t))) || null } _setInitialAttributes(t, e) { this._setAttributeIfNotExists(t, "role", "tablist"); for (const t of e) this._setInitialAttributesOnChild(t) } _setInitialAttributesOnChild(t) { t = this._getInnerElement(t); const e = this._elemIsActive(t), i = this._getOuterElement(t); t.setAttribute("aria-selected", e), i !== t && this._setAttributeIfNotExists(i, "role", "presentation"), e || t.setAttribute("tabindex", "-1"), this._setAttributeIfNotExists(t, "role", "tab"), this._setInitialAttributesOnTargetPanel(t) } _setInitialAttributesOnTargetPanel(t) { const e = K.getElementFromSelector(t); e && (this._setAttributeIfNotExists(e, "role", "tabpanel"), t.id && this._setAttributeIfNotExists(e, "aria-labelledby", `${t.id}`)) } _toggleDropDown(t, e) { const i = this._getOuterElement(t); if (!i.classList.contains("dropdown")) return; const s = (t, s) => { const n = K.findOne(t, i); n && n.classList.toggle(s, e) }; s(ds, ls), s(".dropdown-menu", hs), i.setAttribute("aria-expanded", e) } _setAttributeIfNotExists(t, e, i) { t.hasAttribute(e) || t.setAttribute(e, i) } _elemIsActive(t) { return t.classList.contains(ls) } _getInnerElement(t) { return t.matches(gs) ? t : K.findOne(gs, t) } _getOuterElement(t) { return t.closest(".nav-item, .list-group-item") || t } static jQueryInterface(t) { return this.each((function () { const e = ms.getOrCreateInstance(this); if ("string" == typeof t) { if (void 0 === e[t] || t.startsWith("_") || "constructor" === t) throw new TypeError(`No method named "${t}"`); e[t]() } })) } } j.on(document, Zi, _s, (function (t) { ["A", "AREA"].includes(this.tagName) && t.preventDefault(), d(this) || ms.getOrCreateInstance(this).show() })), j.on(window, es, (() => { for (const t of K.find(fs)) ms.getOrCreateInstance(t) })), b(ms); const ps = ".bs.toast", bs = `mouseover${ps}`, vs = `mouseout${ps}`, ys = `focusin${ps}`, ws = `focusout${ps}`, As = `hide${ps}`, Es = `hidden${ps}`, Cs = `show${ps}`, Ts = `shown${ps}`, ks = "hide", $s = "show", Ss = "showing", Ls = { animation: "boolean", autohide: "boolean", delay: "number" }, Os = { animation: !0, autohide: !0, delay: 5e3 }; class Is extends W { constructor(t, e) { super(t, e), this._timeout = null, this._hasMouseInteraction = !1, this._hasKeyboardInteraction = !1, this._setListeners() } static get Default() { return Os } static get DefaultType() { return Ls } static get NAME() { return "toast" } show() { j.trigger(this._element, Cs).defaultPrevented || (this._clearTimeout(), this._config.animation && this._element.classList.add("fade"), this._element.classList.remove(ks), g(this._element), this._element.classList.add($s, Ss), this._queueCallback((() => { this._element.classList.remove(Ss), j.trigger(this._element, Ts), this._maybeScheduleHide() }), this._element, this._config.animation)) } hide() { this.isShown() && (j.trigger(this._element, As).defaultPrevented || (this._element.classList.add(Ss), this._queueCallback((() => { this._element.classList.add(ks), this._element.classList.remove(Ss, $s), j.trigger(this._element, Es) }), this._element, this._config.animation))) } dispose() { this._clearTimeout(), this.isShown() && this._element.classList.remove($s), super.dispose() } isShown() { return this._element.classList.contains($s) } _maybeScheduleHide() { this._config.autohide && (this._hasMouseInteraction || this._hasKeyboardInteraction || (this._timeout = setTimeout((() => { this.hide() }), this._config.delay))) } _onInteraction(t, e) { switch (t.type) { case "mouseover": case "mouseout": this._hasMouseInteraction = e; break; case "focusin": case "focusout": this._hasKeyboardInteraction = e }if (e) return void this._clearTimeout(); const i = t.relatedTarget; this._element === i || this._element.contains(i) || this._maybeScheduleHide() } _setListeners() { j.on(this._element, bs, (t => this._onInteraction(t, !0))), j.on(this._element, vs, (t => this._onInteraction(t, !1))), j.on(this._element, ys, (t => this._onInteraction(t, !0))), j.on(this._element, ws, (t => this._onInteraction(t, !1))) } _clearTimeout() { clearTimeout(this._timeout), this._timeout = null } static jQueryInterface(t) { return this.each((function () { const e = Is.getOrCreateInstance(this, t); if ("string" == typeof t) { if (void 0 === e[t]) throw new TypeError(`No method named "${t}"`); e[t](this) } })) } } return V(Is), b(Is), { Alert: U, Button: J, Carousel: Ot, Collapse: Rt, Dropdown: fe, Modal: Ue, Offcanvas: gi, Popover: Mi, ScrollSpy: Qi, Tab: ms, Toast: Is, Tooltip: Ni } })); +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("@popperjs/core")):"function"==typeof define&&define.amd?define(["@popperjs/core"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap=e(t.Popper)}(this,(function(t){"use strict";function e(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t)for(const i in t)if("default"!==i){const s=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(e,i,s.get?s:{enumerable:!0,get:()=>t[i]})}return e.default=t,Object.freeze(e)}const i=e(t),s=new Map,n={set(t,e,i){s.has(t)||s.set(t,new Map);const n=s.get(t);n.has(e)||0===n.size?n.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(n.keys())[0]}.`)},get:(t,e)=>s.has(t)&&s.get(t).get(e)||null,remove(t,e){if(!s.has(t))return;const i=s.get(t);i.delete(e),0===i.size&&s.delete(t)}},o="transitionend",r=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),a=t=>{t.dispatchEvent(new Event(o))},l=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),c=t=>l(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(r(t)):null,h=t=>{if(!l(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},d=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),u=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?u(t.parentNode):null},_=()=>{},g=t=>{t.offsetHeight},f=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,m=[],p=()=>"rtl"===document.documentElement.dir,b=t=>{var e;e=()=>{const e=f();if(e){const i=t.NAME,s=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=s,t.jQueryInterface)}},"loading"===document.readyState?(m.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of m)t()})),m.push(e)):e()},v=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,y=(t,e,i=!0)=>{if(!i)return void v(t);const s=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const s=Number.parseFloat(e),n=Number.parseFloat(i);return s||n?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let n=!1;const r=({target:i})=>{i===e&&(n=!0,e.removeEventListener(o,r),v(t))};e.addEventListener(o,r),setTimeout((()=>{n||a(e)}),s)},w=(t,e,i,s)=>{const n=t.length;let o=t.indexOf(e);return-1===o?!i&&s?t[n-1]:t[0]:(o+=i?1:-1,s&&(o=(o+n)%n),t[Math.max(0,Math.min(o,n-1))])},A=/[^.]*(?=\..*)\.|.*/,E=/\..*/,C=/::\d+$/,T={};let k=1;const $={mouseenter:"mouseover",mouseleave:"mouseout"},S=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function L(t,e){return e&&`${e}::${k++}`||t.uidEvent||k++}function O(t){const e=L(t);return t.uidEvent=e,T[e]=T[e]||{},T[e]}function I(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function D(t,e,i){const s="string"==typeof e,n=s?i:e||i;let o=M(t);return S.has(o)||(o=t),[s,n,o]}function N(t,e,i,s,n){if("string"!=typeof e||!t)return;let[o,r,a]=D(e,i,s);if(e in $){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=O(t),c=l[a]||(l[a]={}),h=I(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&n);const d=L(r,e.replace(A,"")),u=o?function(t,e,i){return function s(n){const o=t.querySelectorAll(e);for(let{target:r}=n;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return F(n,{delegateTarget:r}),s.oneOff&&j.off(t,n.type,e,i),i.apply(r,[n])}}(t,i,r):function(t,e){return function i(s){return F(s,{delegateTarget:t}),i.oneOff&&j.off(t,s.type,e),e.apply(t,[s])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=n,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function P(t,e,i,s,n){const o=I(e[i],s,n);o&&(t.removeEventListener(i,o,Boolean(n)),delete e[i][o.uidEvent])}function x(t,e,i,s){const n=e[i]||{};for(const[o,r]of Object.entries(n))o.includes(s)&&P(t,e,i,r.callable,r.delegationSelector)}function M(t){return t=t.replace(E,""),$[t]||t}const j={on(t,e,i,s){N(t,e,i,s,!1)},one(t,e,i,s){N(t,e,i,s,!0)},off(t,e,i,s){if("string"!=typeof e||!t)return;const[n,o,r]=D(e,i,s),a=r!==e,l=O(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))x(t,l,i,e.slice(1));for(const[i,s]of Object.entries(c)){const n=i.replace(C,"");a&&!e.includes(n)||P(t,l,r,s.callable,s.delegationSelector)}}else{if(!Object.keys(c).length)return;P(t,l,r,o,n?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const s=f();let n=null,o=!0,r=!0,a=!1;e!==M(e)&&s&&(n=s.Event(e,i),s(t).trigger(n),o=!n.isPropagationStopped(),r=!n.isImmediatePropagationStopped(),a=n.isDefaultPrevented());const l=F(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&n&&n.preventDefault(),l}};function F(t,e={}){for(const[i,s]of Object.entries(e))try{t[i]=s}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>s})}return t}function z(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function H(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const B={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${H(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${H(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const s of i){let i=s.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=z(t.dataset[s])}return e},getDataAttribute:(t,e)=>z(t.getAttribute(`data-bs-${H(e)}`))};class q{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=l(e)?B.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...l(e)?B.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[s,n]of Object.entries(e)){const e=t[s],o=l(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(n).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${s}" provided type "${o}" but expected type "${n}".`)}var i}}class W extends q{constructor(t,e){super(),(t=c(t))&&(this._element=t,this._config=this._getConfig(e),n.set(this._element,this.constructor.DATA_KEY,this))}dispose(){n.remove(this._element,this.constructor.DATA_KEY),j.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){y(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return n.get(c(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const R=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>r(t))).join(","):null},K={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let s=t.parentNode.closest(e);for(;s;)i.push(s),s=s.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!d(t)&&h(t)))},getSelectorFromElement(t){const e=R(t);return e&&K.findOne(e)?e:null},getElementFromSelector(t){const e=R(t);return e?K.findOne(e):null},getMultipleElementsFromSelector(t){const e=R(t);return e?K.find(e):[]}},V=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;j.on(document,i,`[data-bs-dismiss="${s}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),d(this))return;const n=K.getElementFromSelector(this)||this.closest(`.${s}`);t.getOrCreateInstance(n)[e]()}))},Q=".bs.alert",X=`close${Q}`,Y=`closed${Q}`;class U extends W{static get NAME(){return"alert"}close(){if(j.trigger(this._element,X).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),j.trigger(this._element,Y),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=U.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}V(U,"close"),b(U);const G='[data-bs-toggle="button"]';class J extends W{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=J.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}j.on(document,"click.bs.button.data-api",G,(t=>{t.preventDefault();const e=t.target.closest(G);J.getOrCreateInstance(e).toggle()})),b(J);const Z=".bs.swipe",tt=`touchstart${Z}`,et=`touchmove${Z}`,it=`touchend${Z}`,st=`pointerdown${Z}`,nt=`pointerup${Z}`,ot={endCallback:null,leftCallback:null,rightCallback:null},rt={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class at extends q{constructor(t,e){super(),this._element=t,t&&at.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return ot}static get DefaultType(){return rt}static get NAME(){return"swipe"}dispose(){j.off(this._element,Z)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),v(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&v(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(j.on(this._element,st,(t=>this._start(t))),j.on(this._element,nt,(t=>this._end(t))),this._element.classList.add("pointer-event")):(j.on(this._element,tt,(t=>this._start(t))),j.on(this._element,et,(t=>this._move(t))),j.on(this._element,it,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const lt=".bs.carousel",ct=".data-api",ht="next",dt="prev",ut="left",_t="right",gt=`slide${lt}`,ft=`slid${lt}`,mt=`keydown${lt}`,pt=`mouseenter${lt}`,bt=`mouseleave${lt}`,vt=`dragstart${lt}`,yt=`load${lt}${ct}`,wt=`click${lt}${ct}`,At="carousel",Et="active",Ct=".active",Tt=".carousel-item",kt=Ct+Tt,$t={ArrowLeft:_t,ArrowRight:ut},St={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},Lt={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class Ot extends W{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=K.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===At&&this.cycle()}static get Default(){return St}static get DefaultType(){return Lt}static get NAME(){return"carousel"}next(){this._slide(ht)}nextWhenVisible(){!document.hidden&&h(this._element)&&this.next()}prev(){this._slide(dt)}pause(){this._isSliding&&a(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?j.one(this._element,ft,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void j.one(this._element,ft,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const s=t>i?ht:dt;this._slide(s,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&j.on(this._element,mt,(t=>this._keydown(t))),"hover"===this._config.pause&&(j.on(this._element,pt,(()=>this.pause())),j.on(this._element,bt,(()=>this._maybeEnableCycle()))),this._config.touch&&at.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of K.find(".carousel-item img",this._element))j.on(t,vt,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(ut)),rightCallback:()=>this._slide(this._directionToOrder(_t)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new at(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=$t[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=K.findOne(Ct,this._indicatorsElement);e.classList.remove(Et),e.removeAttribute("aria-current");const i=K.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(Et),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),s=t===ht,n=e||w(this._getItems(),i,s,this._config.wrap);if(n===i)return;const o=this._getItemIndex(n),r=e=>j.trigger(this._element,e,{relatedTarget:n,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(gt).defaultPrevented)return;if(!i||!n)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=n;const l=s?"carousel-item-start":"carousel-item-end",c=s?"carousel-item-next":"carousel-item-prev";n.classList.add(c),g(n),i.classList.add(l),n.classList.add(l),this._queueCallback((()=>{n.classList.remove(l,c),n.classList.add(Et),i.classList.remove(Et,c,l),this._isSliding=!1,r(ft)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return K.findOne(kt,this._element)}_getItems(){return K.find(Tt,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return p()?t===ut?dt:ht:t===ut?ht:dt}_orderToDirection(t){return p()?t===dt?ut:_t:t===dt?_t:ut}static jQueryInterface(t){return this.each((function(){const e=Ot.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}j.on(document,wt,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=K.getElementFromSelector(this);if(!e||!e.classList.contains(At))return;t.preventDefault();const i=Ot.getOrCreateInstance(e),s=this.getAttribute("data-bs-slide-to");return s?(i.to(s),void i._maybeEnableCycle()):"next"===B.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),j.on(window,yt,(()=>{const t=K.find('[data-bs-ride="carousel"]');for(const e of t)Ot.getOrCreateInstance(e)})),b(Ot);const It=".bs.collapse",Dt=`show${It}`,Nt=`shown${It}`,Pt=`hide${It}`,xt=`hidden${It}`,Mt=`click${It}.data-api`,jt="show",Ft="collapse",zt="collapsing",Ht=`:scope .${Ft} .${Ft}`,Bt='[data-bs-toggle="collapse"]',qt={parent:null,toggle:!0},Wt={parent:"(null|element)",toggle:"boolean"};class Rt extends W{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=K.find(Bt);for(const t of i){const e=K.getSelectorFromElement(t),i=K.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return qt}static get DefaultType(){return Wt}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Rt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(j.trigger(this._element,Dt).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(Ft),this._element.classList.add(zt),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(zt),this._element.classList.add(Ft,jt),this._element.style[e]="",j.trigger(this._element,Nt)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(j.trigger(this._element,Pt).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,g(this._element),this._element.classList.add(zt),this._element.classList.remove(Ft,jt);for(const t of this._triggerArray){const e=K.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(zt),this._element.classList.add(Ft),j.trigger(this._element,xt)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(jt)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=c(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(Bt);for(const e of t){const t=K.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=K.find(Ht,this._config.parent);return K.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Rt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}j.on(document,Mt,Bt,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of K.getMultipleElementsFromSelector(this))Rt.getOrCreateInstance(t,{toggle:!1}).toggle()})),b(Rt);const Kt="dropdown",Vt=".bs.dropdown",Qt=".data-api",Xt="ArrowUp",Yt="ArrowDown",Ut=`hide${Vt}`,Gt=`hidden${Vt}`,Jt=`show${Vt}`,Zt=`shown${Vt}`,te=`click${Vt}${Qt}`,ee=`keydown${Vt}${Qt}`,ie=`keyup${Vt}${Qt}`,se="show",ne='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',oe=`${ne}.${se}`,re=".dropdown-menu",ae=p()?"top-end":"top-start",le=p()?"top-start":"top-end",ce=p()?"bottom-end":"bottom-start",he=p()?"bottom-start":"bottom-end",de=p()?"left-start":"right-start",ue=p()?"right-start":"left-start",_e={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},ge={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class fe extends W{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=K.next(this._element,re)[0]||K.prev(this._element,re)[0]||K.findOne(re,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return _e}static get DefaultType(){return ge}static get NAME(){return Kt}toggle(){return this._isShown()?this.hide():this.show()}show(){if(d(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!j.trigger(this._element,Jt,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))j.on(t,"mouseover",_);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(se),this._element.classList.add(se),j.trigger(this._element,Zt,t)}}hide(){if(d(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!j.trigger(this._element,Ut,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))j.off(t,"mouseover",_);this._popper&&this._popper.destroy(),this._menu.classList.remove(se),this._element.classList.remove(se),this._element.setAttribute("aria-expanded","false"),B.removeDataAttribute(this._menu,"popper"),j.trigger(this._element,Gt,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!l(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Kt.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===i)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:l(this._config.reference)?t=c(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const e=this._getPopperConfig();this._popper=i.createPopper(t,this._menu,e)}_isShown(){return this._menu.classList.contains(se)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return de;if(t.classList.contains("dropstart"))return ue;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?le:ae:e?he:ce}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(B.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...v(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=K.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>h(t)));i.length&&w(i,e,t===Yt,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=fe.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=K.find(oe);for(const i of e){const e=fe.getInstance(i);if(!e||!1===e._config.autoClose)continue;const s=t.composedPath(),n=s.includes(e._menu);if(s.includes(e._element)||"inside"===e._config.autoClose&&!n||"outside"===e._config.autoClose&&n)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,s=[Xt,Yt].includes(t.key);if(!s&&!i)return;if(e&&!i)return;t.preventDefault();const n=this.matches(ne)?this:K.prev(this,ne)[0]||K.next(this,ne)[0]||K.findOne(ne,t.delegateTarget.parentNode),o=fe.getOrCreateInstance(n);if(s)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),n.focus())}}j.on(document,ee,ne,fe.dataApiKeydownHandler),j.on(document,ee,re,fe.dataApiKeydownHandler),j.on(document,te,fe.clearMenus),j.on(document,ie,fe.clearMenus),j.on(document,te,ne,(function(t){t.preventDefault(),fe.getOrCreateInstance(this).toggle()})),b(fe);const me="backdrop",pe="show",be=`mousedown.bs.${me}`,ve={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},ye={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class we extends q{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return ve}static get DefaultType(){return ye}static get NAME(){return me}show(t){if(!this._config.isVisible)return void v(t);this._append();const e=this._getElement();this._config.isAnimated&&g(e),e.classList.add(pe),this._emulateAnimation((()=>{v(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(pe),this._emulateAnimation((()=>{this.dispose(),v(t)}))):v(t)}dispose(){this._isAppended&&(j.off(this._element,be),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=c(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),j.on(t,be,(()=>{v(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){y(t,this._getElement(),this._config.isAnimated)}}const Ae=".bs.focustrap",Ee=`focusin${Ae}`,Ce=`keydown.tab${Ae}`,Te="backward",ke={autofocus:!0,trapElement:null},$e={autofocus:"boolean",trapElement:"element"};class Se extends q{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return ke}static get DefaultType(){return $e}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),j.off(document,Ae),j.on(document,Ee,(t=>this._handleFocusin(t))),j.on(document,Ce,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,j.off(document,Ae))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=K.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===Te?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?Te:"forward")}}const Le=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",Oe=".sticky-top",Ie="padding-right",De="margin-right";class Ne{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,Ie,(e=>e+t)),this._setElementAttributes(Le,Ie,(e=>e+t)),this._setElementAttributes(Oe,De,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,Ie),this._resetElementAttributes(Le,Ie),this._resetElementAttributes(Oe,De)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const s=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+s)return;this._saveInitialAttribute(t,e);const n=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(n))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&B.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=B.getDataAttribute(t,e);null!==i?(B.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(l(t))e(t);else for(const i of K.find(t,this._element))e(i)}}const Pe=".bs.modal",xe=`hide${Pe}`,Me=`hidePrevented${Pe}`,je=`hidden${Pe}`,Fe=`show${Pe}`,ze=`shown${Pe}`,He=`resize${Pe}`,Be=`click.dismiss${Pe}`,qe=`mousedown.dismiss${Pe}`,We=`keydown.dismiss${Pe}`,Re=`click${Pe}.data-api`,Ke="modal-open",Ve="show",Qe="modal-static",Xe={backdrop:!0,focus:!0,keyboard:!0},Ye={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ue extends W{constructor(t,e){super(t,e),this._dialog=K.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new Ne,this._addEventListeners()}static get Default(){return Xe}static get DefaultType(){return Ye}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||j.trigger(this._element,Fe,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Ke),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(j.trigger(this._element,xe).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Ve),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){j.off(window,Pe),j.off(this._dialog,Pe),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new we({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Se({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=K.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),g(this._element),this._element.classList.add(Ve),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,j.trigger(this._element,ze,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){j.on(this._element,We,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),j.on(window,He,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),j.on(this._element,qe,(t=>{j.one(this._element,Be,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Ke),this._resetAdjustments(),this._scrollBar.reset(),j.trigger(this._element,je)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(j.trigger(this._element,Me).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(Qe)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(Qe),this._queueCallback((()=>{this._element.classList.remove(Qe),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=p()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=p()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ue.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}j.on(document,Re,'[data-bs-toggle="modal"]',(function(t){const e=K.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),j.one(e,Fe,(t=>{t.defaultPrevented||j.one(e,je,(()=>{h(this)&&this.focus()}))}));const i=K.findOne(".modal.show");i&&Ue.getInstance(i).hide(),Ue.getOrCreateInstance(e).toggle(this)})),V(Ue),b(Ue);const Ge=".bs.offcanvas",Je=".data-api",Ze=`load${Ge}${Je}`,ti="show",ei="showing",ii="hiding",si=".offcanvas.show",ni=`show${Ge}`,oi=`shown${Ge}`,ri=`hide${Ge}`,ai=`hidePrevented${Ge}`,li=`hidden${Ge}`,ci=`resize${Ge}`,hi=`click${Ge}${Je}`,di=`keydown.dismiss${Ge}`,ui={backdrop:!0,keyboard:!0,scroll:!1},_i={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class gi extends W{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return ui}static get DefaultType(){return _i}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||j.trigger(this._element,ni,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new Ne).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(ei),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(ti),this._element.classList.remove(ei),j.trigger(this._element,oi,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(j.trigger(this._element,ri).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(ii),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(ti,ii),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new Ne).reset(),j.trigger(this._element,li)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new we({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():j.trigger(this._element,ai)}:null})}_initializeFocusTrap(){return new Se({trapElement:this._element})}_addEventListeners(){j.on(this._element,di,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():j.trigger(this._element,ai))}))}static jQueryInterface(t){return this.each((function(){const e=gi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}j.on(document,hi,'[data-bs-toggle="offcanvas"]',(function(t){const e=K.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),d(this))return;j.one(e,li,(()=>{h(this)&&this.focus()}));const i=K.findOne(si);i&&i!==e&&gi.getInstance(i).hide(),gi.getOrCreateInstance(e).toggle(this)})),j.on(window,Ze,(()=>{for(const t of K.find(si))gi.getOrCreateInstance(t).show()})),j.on(window,ci,(()=>{for(const t of K.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&gi.getOrCreateInstance(t).hide()})),V(gi),b(gi);const fi={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},mi=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),pi=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,bi=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!mi.has(i)||Boolean(pi.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},vi={allowList:fi,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
                                                                                                                                                                                                                                                                                                          "},yi={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},wi={entry:"(string|element|function|null)",selector:"(string|element)"};class Ai extends q{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return vi}static get DefaultType(){return yi}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},wi)}_setContent(t,e,i){const s=K.findOne(i,t);s&&((e=this._resolvePossibleFunction(e))?l(e)?this._putElementInTemplate(c(e),s):this._config.html?s.innerHTML=this._maybeSanitize(e):s.textContent=e:s.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const s=(new window.DOMParser).parseFromString(t,"text/html"),n=[].concat(...s.body.querySelectorAll("*"));for(const t of n){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const s=[].concat(...t.attributes),n=[].concat(e["*"]||[],e[i]||[]);for(const e of s)bi(e,n)||t.removeAttribute(e.nodeName)}return s.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return v(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Ei=new Set(["sanitize","allowList","sanitizeFn"]),Ci="fade",Ti="show",ki=".modal",$i="hide.bs.modal",Si="hover",Li="focus",Oi={AUTO:"auto",TOP:"top",RIGHT:p()?"left":"right",BOTTOM:"bottom",LEFT:p()?"right":"left"},Ii={allowList:fi,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},Di={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class Ni extends W{constructor(t,e){if(void 0===i)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,e),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return Ii}static get DefaultType(){return Di}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),j.off(this._element.closest(ki),$i,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=j.trigger(this._element,this.constructor.eventName("show")),e=(u(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:s}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(s.append(i),j.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(Ti),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))j.on(t,"mouseover",_);this._queueCallback((()=>{j.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!j.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(Ti),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))j.off(t,"mouseover",_);this._activeTrigger.click=!1,this._activeTrigger[Li]=!1,this._activeTrigger[Si]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),j.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(Ci,Ti),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(Ci),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Ai({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{".tooltip-inner":this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(Ci)}_isShown(){return this.tip&&this.tip.classList.contains(Ti)}_createPopper(t){const e=v(this._config.placement,[this,t,this._element]),s=Oi[e.toUpperCase()];return i.createPopper(this._element,t,this._getPopperConfig(s))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return v(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...v(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)j.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===Si?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===Si?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");j.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?Li:Si]=!0,e._enter()})),j.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?Li:Si]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},j.on(this._element.closest(ki),$i,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=B.getDataAttributes(this._element);for(const t of Object.keys(e))Ei.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:c(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=Ni.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}b(Ni);const Pi={...Ni.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},xi={...Ni.DefaultType,content:"(null|string|element|function)"};class Mi extends Ni{static get Default(){return Pi}static get DefaultType(){return xi}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{".popover-header":this._getTitle(),".popover-body":this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=Mi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}b(Mi);const ji=".bs.scrollspy",Fi=`activate${ji}`,zi=`click${ji}`,Hi=`load${ji}.data-api`,Bi="active",qi="[href]",Wi=".nav-link",Ri=`${Wi}, .nav-item > ${Wi}, .list-group-item`,Ki={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},Vi={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Qi extends W{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return Ki}static get DefaultType(){return Vi}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=c(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(j.off(this._config.target,zi),j.on(this._config.target,zi,qi,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,s=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:s,behavior:"smooth"});i.scrollTop=s}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},s=(this._rootElement||document.documentElement).scrollTop,n=s>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=s;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(n&&t){if(i(o),!s)return}else n||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=K.find(qi,this._config.target);for(const e of t){if(!e.hash||d(e))continue;const t=K.findOne(decodeURI(e.hash),this._element);h(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(Bi),this._activateParents(t),j.trigger(this._element,Fi,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))K.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(Bi);else for(const e of K.parents(t,".nav, .list-group"))for(const t of K.prev(e,Ri))t.classList.add(Bi)}_clearActiveClass(t){t.classList.remove(Bi);const e=K.find(`${qi}.${Bi}`,t);for(const t of e)t.classList.remove(Bi)}static jQueryInterface(t){return this.each((function(){const e=Qi.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(window,Hi,(()=>{for(const t of K.find('[data-bs-spy="scroll"]'))Qi.getOrCreateInstance(t)})),b(Qi);const Xi=".bs.tab",Yi=`hide${Xi}`,Ui=`hidden${Xi}`,Gi=`show${Xi}`,Ji=`shown${Xi}`,Zi=`click${Xi}`,ts=`keydown${Xi}`,es=`load${Xi}`,is="ArrowLeft",ss="ArrowRight",ns="ArrowUp",os="ArrowDown",rs="Home",as="End",ls="active",cs="fade",hs="show",ds=".dropdown-toggle",us=`:not(${ds})`,_s='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',gs=`.nav-link${us}, .list-group-item${us}, [role="tab"]${us}, ${_s}`,fs=`.${ls}[data-bs-toggle="tab"], .${ls}[data-bs-toggle="pill"], .${ls}[data-bs-toggle="list"]`;class ms extends W{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),j.on(this._element,ts,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?j.trigger(e,Yi,{relatedTarget:t}):null;j.trigger(t,Gi,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(ls),this._activate(K.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),j.trigger(t,Ji,{relatedTarget:e})):t.classList.add(hs)}),t,t.classList.contains(cs)))}_deactivate(t,e){t&&(t.classList.remove(ls),t.blur(),this._deactivate(K.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),j.trigger(t,Ui,{relatedTarget:e})):t.classList.remove(hs)}),t,t.classList.contains(cs)))}_keydown(t){if(![is,ss,ns,os,rs,as].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!d(t)));let i;if([rs,as].includes(t.key))i=e[t.key===rs?0:e.length-1];else{const s=[ss,os].includes(t.key);i=w(e,t.target,s,!0)}i&&(i.focus({preventScroll:!0}),ms.getOrCreateInstance(i).show())}_getChildren(){return K.find(gs,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=K.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const s=(t,s)=>{const n=K.findOne(t,i);n&&n.classList.toggle(s,e)};s(ds,ls),s(".dropdown-menu",hs),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(ls)}_getInnerElement(t){return t.matches(gs)?t:K.findOne(gs,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=ms.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}j.on(document,Zi,_s,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),d(this)||ms.getOrCreateInstance(this).show()})),j.on(window,es,(()=>{for(const t of K.find(fs))ms.getOrCreateInstance(t)})),b(ms);const ps=".bs.toast",bs=`mouseover${ps}`,vs=`mouseout${ps}`,ys=`focusin${ps}`,ws=`focusout${ps}`,As=`hide${ps}`,Es=`hidden${ps}`,Cs=`show${ps}`,Ts=`shown${ps}`,ks="hide",$s="show",Ss="showing",Ls={animation:"boolean",autohide:"boolean",delay:"number"},Os={animation:!0,autohide:!0,delay:5e3};class Is extends W{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return Os}static get DefaultType(){return Ls}static get NAME(){return"toast"}show(){j.trigger(this._element,Cs).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(ks),g(this._element),this._element.classList.add($s,Ss),this._queueCallback((()=>{this._element.classList.remove(Ss),j.trigger(this._element,Ts),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(j.trigger(this._element,As).defaultPrevented||(this._element.classList.add(Ss),this._queueCallback((()=>{this._element.classList.add(ks),this._element.classList.remove(Ss,$s),j.trigger(this._element,Es)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove($s),super.dispose()}isShown(){return this._element.classList.contains($s)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){j.on(this._element,bs,(t=>this._onInteraction(t,!0))),j.on(this._element,vs,(t=>this._onInteraction(t,!1))),j.on(this._element,ys,(t=>this._onInteraction(t,!0))),j.on(this._element,ws,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=Is.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}return V(Is),b(Is),{Alert:U,Button:J,Carousel:Ot,Collapse:Rt,Dropdown:fe,Modal:Ue,Offcanvas:gi,Popover:Mi,ScrollSpy:Qi,Tab:ms,Toast:Is,Tooltip:Ni}})); //# sourceMappingURL=bootstrap.min.js.map \ No newline at end of file diff --git a/src/WebExpress.WebUI/Assets/js/bootstrap.min.js.map b/src/WebExpress.WebUI/Assets/js/bootstrap.min.js.map index 9efcd6b4..4d437152 100644 --- a/src/WebExpress.WebUI/Assets/js/bootstrap.min.js.map +++ b/src/WebExpress.WebUI/Assets/js/bootstrap.min.js.map @@ -1 +1 @@ -{"version":3,"names":["elementMap","Map","Data","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete","TRANSITION_END","parseSelector","selector","window","CSS","escape","replace","match","id","triggerTransitionEnd","dispatchEvent","Event","isElement","object","jquery","nodeType","getElement","length","document","querySelector","isVisible","getClientRects","elementIsVisible","getComputedStyle","getPropertyValue","closedDetails","closest","summary","parentNode","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","noop","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","isRTL","dir","defineJQueryPlugin","plugin","callback","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","readyState","addEventListener","push","execute","possibleCallback","args","defaultValue","executeAfterTransition","transitionElement","waitForTransition","emulatedDuration","transitionDuration","transitionDelay","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","split","getTransitionDurationFromElement","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","listLength","index","indexOf","Math","max","min","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","nativeEvents","Set","makeEventUid","uid","getElementEvents","findHandler","events","callable","delegationSelector","Object","values","find","event","normalizeParameters","originalTypeEvent","delegationFunction","isDelegated","typeEvent","getTypeEvent","addHandler","oneOff","wrapFunction","relatedTarget","delegateTarget","call","this","handlers","previousFunction","domElements","querySelectorAll","domElement","hydrateObj","EventHandler","off","type","apply","bootstrapDelegationHandler","bootstrapHandler","removeHandler","Boolean","removeNamespacedHandlers","namespace","storeElementEvent","handlerKey","entries","includes","on","one","inNamespace","isNamespace","startsWith","elementEvent","slice","keyHandlers","trigger","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","evt","cancelable","preventDefault","obj","meta","value","_unused","defineProperty","configurable","normalizeData","toString","JSON","parse","decodeURIComponent","normalizeDataKey","chr","toLowerCase","Manipulator","setDataAttribute","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","dataset","filter","pureKey","charAt","getDataAttribute","Config","Default","DefaultType","Error","_getConfig","config","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","jsonConfig","constructor","configTypes","property","expectedTypes","valueType","prototype","RegExp","test","TypeError","toUpperCase","BaseComponent","super","_element","_config","DATA_KEY","dispose","EVENT_KEY","propertyName","getOwnPropertyNames","_queueCallback","isAnimated","getInstance","getOrCreateInstance","VERSION","eventName","getSelector","hrefAttribute","trim","SelectorEngine","concat","Element","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","map","join","el","getSelectorFromElement","getElementFromSelector","getMultipleElementsFromSelector","enableDismissTrigger","component","method","clickEvent","tagName","EVENT_CLOSE","EVENT_CLOSED","Alert","close","_destroyElement","each","data","undefined","SELECTOR_DATA_TOGGLE","Button","toggle","button","EVENT_TOUCHSTART","EVENT_TOUCHMOVE","EVENT_TOUCHEND","EVENT_POINTERDOWN","EVENT_POINTERUP","endCallback","leftCallback","rightCallback","Swipe","isSupported","_deltaX","_supportPointerEvents","PointerEvent","_initEvents","_start","_eventIsPointerPenTouch","clientX","touches","_end","_handleSwipe","_move","absDeltaX","abs","direction","add","pointerType","navigator","maxTouchPoints","DATA_API_KEY","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","EVENT_SLIDE","EVENT_SLID","EVENT_KEYDOWN","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","EVENT_DRAG_START","EVENT_LOAD_DATA_API","EVENT_CLICK_DATA_API","CLASS_NAME_CAROUSEL","CLASS_NAME_ACTIVE","SELECTOR_ACTIVE","SELECTOR_ITEM","SELECTOR_ACTIVE_ITEM","KEY_TO_DIRECTION","ArrowLeft","ArrowRight","interval","keyboard","pause","ride","touch","wrap","Carousel","_interval","_activeElement","_isSliding","touchTimeout","_swipeHelper","_indicatorsElement","_addEventListeners","cycle","_slide","nextWhenVisible","hidden","_clearInterval","_updateInterval","setInterval","_maybeEnableCycle","to","items","_getItems","activeIndex","_getItemIndex","_getActive","order","defaultInterval","_keydown","_addTouchEventListeners","img","swipeConfig","_directionToOrder","endCallBack","clearTimeout","_setActiveIndicatorElement","activeIndicator","newActiveIndicator","elementInterval","parseInt","isNext","nextElement","nextElementIndex","triggerEvent","_orderToDirection","isCycling","directionalClassName","orderClassName","completeCallBack","_isAnimated","clearInterval","carousel","slideIndex","carousels","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","CLASS_NAME_SHOW","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_DEEPER_CHILDREN","parent","Collapse","_isTransitioning","_triggerArray","toggleList","elem","filterElement","foundElement","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","activeChildren","_getFirstLevelChildren","activeInstance","dimension","_getDimension","style","scrollSize","complete","getBoundingClientRect","selected","triggerArray","isOpen","ARROW_UP_KEY","ARROW_DOWN_KEY","EVENT_KEYDOWN_DATA_API","EVENT_KEYUP_DATA_API","SELECTOR_DATA_TOGGLE_SHOWN","SELECTOR_MENU","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","autoClose","boundary","display","offset","popperConfig","reference","Dropdown","_popper","_parent","_menu","_inNavbar","_detectNavbar","_createPopper","focus","_completeHide","destroy","update","Popper","referenceElement","_getPopperConfig","createPopper","_getPlacement","parentDropdown","isEnd","_getOffset","popperData","defaultBsPopperConfig","placement","modifiers","options","enabled","_selectMenuItem","clearMenus","openToggles","context","composedPath","isMenuTarget","dataApiKeydownHandler","isInput","isEscapeEvent","isUpOrDownEvent","getToggleButton","stopPropagation","EVENT_MOUSEDOWN","className","clickCallback","rootElement","Backdrop","_isAppended","_append","_getElement","_emulateAnimation","backdrop","createElement","append","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_NAV_BACKWARD","autofocus","trapElement","FocusTrap","_isActive","_lastTabNavDirection","activate","_handleFocusin","_handleKeydown","deactivate","elements","shiftKey","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","PROPERTY_PADDING","PROPERTY_MARGIN","ScrollBarHelper","getWidth","documentWidth","clientWidth","innerWidth","width","_disableOverFlow","_setElementAttributes","calculatedValue","reset","_resetElementAttributes","isOverflowing","_saveInitialAttribute","overflow","styleProperty","scrollbarWidth","_applyManipulationCallback","setProperty","actualValue","removeProperty","callBack","sel","EVENT_HIDE_PREVENTED","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_KEYDOWN_DISMISS","CLASS_NAME_OPEN","CLASS_NAME_STATIC","Modal","_dialog","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_scrollBar","_adjustDialog","_showElement","_hideModal","handleUpdate","scrollTop","modalBody","transitionComplete","_triggerBackdropTransition","event2","_resetAdjustments","isModalOverflowing","scrollHeight","clientHeight","initialOverflowY","overflowY","isBodyOverflowing","paddingLeft","paddingRight","showEvent","alreadyOpen","CLASS_NAME_SHOWING","CLASS_NAME_HIDING","OPEN_SELECTOR","scroll","Offcanvas","blur","completeCallback","position","DefaultAllowlist","a","area","b","br","col","code","div","em","hr","h1","h2","h3","h4","h5","h6","i","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","uriAttributes","SAFE_URL_PATTERN","allowedAttribute","attribute","allowedAttributeList","attributeName","nodeName","nodeValue","attributeRegex","some","regex","allowList","content","extraClass","html","sanitize","sanitizeFn","template","DefaultContentType","entry","TemplateFactory","getContent","_resolvePossibleFunction","hasContent","changeContent","_checkContent","toHtml","templateWrapper","innerHTML","_maybeSanitize","text","_setContent","arg","templateElement","_putElementInTemplate","textContent","unsafeHtml","sanitizeFunction","createdDocument","DOMParser","parseFromString","elementName","attributeList","allowedAttributes","sanitizeHtml","DISALLOWED_ATTRIBUTES","CLASS_NAME_FADE","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","animation","container","customClass","delay","fallbackPlacements","title","Tooltip","_isEnabled","_timeout","_isHovered","_activeTrigger","_templateFactory","_newContent","tip","_setListeners","_fixTitle","enable","disable","toggleEnabled","click","_leave","_enter","_hideModalHandler","_disposePopper","_isWithContent","isInTheDom","ownerDocument","_getTipElement","_isWithActiveTrigger","_getTitle","_createTipElement","_getContentForTemplate","_getTemplateFactory","tipId","prefix","floor","random","getElementById","getUID","setContent","_initializeOnDelegatedTarget","_getDelegateConfig","attachment","phase","state","triggers","eventIn","eventOut","_setTimeout","timeout","dataAttributes","dataAttribute","Popover","_getContent","EVENT_ACTIVATE","EVENT_CLICK","SELECTOR_TARGET_LINKS","SELECTOR_NAV_LINKS","SELECTOR_LINK_ITEMS","rootMargin","smoothScroll","threshold","ScrollSpy","_targetLinks","_observableSections","_rootElement","_activeTarget","_observer","_previousScrollData","visibleEntryTop","parentScrollTop","refresh","_initializeTargetsAndObservables","_maybeEnableSmoothScroll","disconnect","_getNewObserver","section","observe","observableSection","hash","height","offsetTop","scrollTo","top","behavior","IntersectionObserver","_observerCallback","targetElement","_process","userScrollsDown","isIntersecting","_clearActiveClass","entryIsLowerThanPrevious","targetLinks","anchor","decodeURI","_activateParents","listGroup","item","activeNodes","node","spy","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","HOME_KEY","END_KEY","SELECTOR_DROPDOWN_TOGGLE","NOT_SELECTOR_DROPDOWN_TOGGLE","SELECTOR_INNER_ELEM","SELECTOR_DATA_TOGGLE_ACTIVE","Tab","_setInitialAttributes","_getChildren","innerElem","_elemIsActive","active","_getActiveElem","hideEvent","_deactivate","_activate","relatedElem","_toggleDropDown","nextActiveElement","preventScroll","_setAttributeIfNotExists","_setInitialAttributesOnChild","_getInnerElement","isActive","outerElem","_getOuterElement","_setInitialAttributesOnTargetPanel","open","EVENT_MOUSEOVER","EVENT_MOUSEOUT","EVENT_FOCUSOUT","CLASS_NAME_HIDE","autohide","Toast","_hasMouseInteraction","_hasKeyboardInteraction","_clearTimeout","_maybeScheduleHide","isShown","_onInteraction","isInteracting"],"sources":["../../js/src/dom/data.js","../../js/src/util/index.js","../../js/src/dom/event-handler.js","../../js/src/dom/manipulator.js","../../js/src/util/config.js","../../js/src/base-component.js","../../js/src/dom/selector-engine.js","../../js/src/util/component-functions.js","../../js/src/alert.js","../../js/src/button.js","../../js/src/util/swipe.js","../../js/src/carousel.js","../../js/src/collapse.js","../../js/src/dropdown.js","../../js/src/util/backdrop.js","../../js/src/util/focustrap.js","../../js/src/util/scrollbar.js","../../js/src/modal.js","../../js/src/offcanvas.js","../../js/src/util/sanitizer.js","../../js/src/util/template-factory.js","../../js/src/tooltip.js","../../js/src/popover.js","../../js/src/scrollspy.js","../../js/src/tab.js","../../js/src/toast.js","../../js/index.umd.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\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","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1_000_000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`)\n }\n\n return selector\n}\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`\n }\n\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false\n }\n\n if (typeof object.jquery !== 'undefined') {\n object = object[0]\n }\n\n return typeof object.nodeType !== 'undefined'\n}\n\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object\n }\n\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object))\n }\n\n return null\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])')\n\n if (!closedDetails) {\n return elementIsVisible\n }\n\n if (closedDetails !== element) {\n const summary = element.closest('summary')\n if (summary && summary.parentNode !== closedDetails) {\n return false\n }\n\n if (summary === null) {\n return false\n }\n }\n\n return elementIsVisible\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight // eslint-disable-line no-unused-expressions\n}\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback()\n }\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]\n }\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n defineJQueryPlugin,\n execute,\n executeAfterTransition,\n findShadowRoot,\n getElement,\n getjQuery,\n getNextActiveElement,\n getTransitionDurationFromElement,\n getUID,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n onDOMContentLoaded,\n parseSelector,\n reflow,\n triggerTransitionEnd,\n toType\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index.js'\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\n\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getElementEvents(element) {\n const uid = makeEventUid(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, { delegateTarget: element })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue\n }\n\n hydrateObj(event, { delegateTarget: target })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n}\n\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events)\n .find(event => event.callable === callable && event.delegationSelector === delegationSelector)\n}\n\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string'\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : (handler || delegationFunction)\n let typeEvent = getTypeEvent(originalTypeEvent)\n\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent\n }\n\n return [isDelegated, callable, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n callable = wrapFunction(callable)\n }\n\n const events = getElementEvents(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)\n\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff\n\n return\n }\n\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = isDelegated ?\n bootstrapDelegationHandler(element, handler, callable) :\n bootstrapHandler(element, callable)\n\n fn.delegationSelector = isDelegated ? handler : null\n fn.callable = callable\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, isDelegated)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false)\n },\n\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getElementEvents(element)\n const storeElementEvent = events[typeEvent] || {}\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return\n }\n\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)\n return\n }\n\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n }\n }\n\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n\n let jQueryEvent = null\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value\n } catch {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value\n }\n })\n }\n }\n\n return obj\n}\n\nexport default EventHandler\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\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 const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\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\nexport default Manipulator\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport { isElement, toType } from './index.js'\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {}\n }\n\n static get DefaultType() {\n return {}\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n return config\n }\n\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n }\n }\n\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property]\n const valueType = isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n }\n }\n}\n\nexport default Config\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data.js'\nimport EventHandler from './dom/event-handler.js'\nimport Config from './util/config.js'\nimport { executeAfterTransition, getElement } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.2'\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super()\n\n element = getElement(element)\n if (!element) {\n return\n }\n\n this._element = element\n this._config = this._getConfig(config)\n\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null\n }\n }\n\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`\n }\n}\n\nexport default BaseComponent\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible, parseSelector } from '../util/index.js'\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`\n }\n\n selector = hrefAttribute && hrefAttribute !== '#' ? parseSelector(hrefAttribute.trim()) : null\n }\n\n return selector\n}\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).filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n let ancestor = element.parentNode.closest(selector)\n\n while (ancestor) {\n parents.push(ancestor)\n ancestor = ancestor.parentNode.closest(selector)\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 // TODO: this is now unused; remove later along with prev()\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 focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n },\n\n getSelectorFromElement(element) {\n const selector = getSelector(element)\n\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null\n }\n\n return null\n },\n\n getElementFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.findOne(selector) : null\n },\n\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.find(selector) : []\n }\n}\n\nexport default SelectorEngine\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isDisabled } from './index.js'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'swipe'\nconst EVENT_KEY = '.bs.swipe'\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n}\n\nconst DefaultType = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n}\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super()\n this._element = element\n\n if (!element || !Swipe.isSupported()) {\n return\n }\n\n this._config = this._getConfig(config)\n this._deltaX = 0\n this._supportPointerEvents = Boolean(window.PointerEvent)\n this._initEvents()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY)\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX\n\n return\n }\n\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX\n }\n }\n\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX\n }\n\n this._handleSwipe()\n execute(this._config.endCallback)\n }\n\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this._deltaX\n }\n\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX)\n\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltaX / this._deltaX\n\n this._deltaX = 0\n\n if (!direction) {\n return\n }\n\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)\n }\n\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))\n }\n }\n\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n }\n}\n\nexport default Swipe\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getNextActiveElement,\n isRTL,\n isVisible,\n reflow,\n triggerTransitionEnd\n} from './util/index.js'\nimport Swipe from './util/swipe.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)', // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._interval = null\n this._activeElement = null\n this._isSliding = false\n this.touchTimeout = null\n this._swipeHelper = null\n\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._addEventListeners()\n\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element)\n }\n\n this._clearInterval()\n }\n\n cycle() {\n this._clearInterval()\n this._updateInterval()\n\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)\n }\n\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle())\n return\n }\n\n this.cycle()\n }\n\n to(index) {\n const items = this._getItems()\n if (index > items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n const activeIndex = this._getItemIndex(this._getActive())\n if (activeIndex === index) {\n return\n }\n\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV\n\n this._slide(order, items[index])\n }\n\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose()\n }\n\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval\n return config\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause())\n EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())\n }\n\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault())\n }\n\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n }\n\n this._swipeHelper = new Swipe(this._element, swipeConfig)\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(this._directionToOrder(direction))\n }\n }\n\n _getItemIndex(element) {\n return this._getItems().indexOf(element)\n }\n\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return\n }\n\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement)\n\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE)\n newActiveIndicator.setAttribute('aria-current', 'true')\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || this._getActive()\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n this._config.interval = elementInterval || this._config.defaultInterval\n }\n\n _slide(order, element = null) {\n if (this._isSliding) {\n return\n }\n\n const activeElement = this._getActive()\n const isNext = order === ORDER_NEXT\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)\n\n if (nextElement === activeElement) {\n return\n }\n\n const nextElementIndex = this._getItemIndex(nextElement)\n\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n })\n }\n\n const slideEvent = triggerEvent(EVENT_SLIDE)\n\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return\n }\n\n const isCycling = Boolean(this._interval)\n this.pause()\n\n this._isSliding = true\n\n this._setActiveIndicatorElement(nextElementIndex)\n this._activeElement = nextElement\n\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n triggerEvent(EVENT_SLID)\n }\n\n this._queueCallback(completeCallBack, activeElement, this._isAnimated())\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE)\n }\n\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n }\n\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element)\n }\n\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n }\n\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config)\n\n if (typeof config === 'number') {\n data.to(config)\n return\n }\n\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n event.preventDefault()\n\n const carousel = Carousel.getOrCreateInstance(target)\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n carousel.to(slideIndex)\n carousel._maybeEnableCycle()\n return\n }\n\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next()\n carousel._maybeEnableCycle()\n return\n }\n\n carousel.prev()\n carousel._maybeEnableCycle()\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getElement,\n reflow\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\nconst Default = {\n parent: null,\n toggle: true\n}\n\nconst DefaultType = {\n parent: '(null|element)',\n toggle: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isTransitioning = false\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElement => foundElement === this._element)\n\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let activeChildren = []\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)\n .filter(element => element !== this._element)\n .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))\n }\n\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n for (const activeInstance of activeChildren) {\n activeInstance.hide()\n }\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger)\n\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)\n\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n }\n }\n\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)\n element.setAttribute('aria-expanded', isOpen)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n execute,\n getElement,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center'\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)'\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR = '.navbar'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\nconst PLACEMENT_TOPCENTER = 'top'\nconst PLACEMENT_BOTTOMCENTER = 'bottom'\n\nconst Default = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n}\n\nconst DefaultType = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n}\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._popper = null\n this._parent = this._element.parentNode // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.findOne(SELECTOR_MENU, this._parent)\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._createPopper()\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = super._getConfig(config)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = this._parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n }\n\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getPlacement() {\n const parentDropdown = this._parent\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {\n return\n }\n\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)\n\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle)\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n const relatedTarget = { relatedTarget: context._element }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName)\n const isEscapeEvent = event.key === ESCAPE_KEY\n const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)\n\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return\n }\n\n if (isInput && !isEscapeEvent) {\n return\n }\n\n event.preventDefault()\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?\n this :\n (SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode))\n\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (isUpOrDownEvent) {\n event.stopPropagation()\n instance.show()\n instance._selectMenuItem(event)\n return\n }\n\n if (instance._isShown()) { // else is escape and we check if it is shown\n event.stopPropagation()\n instance.hide()\n getToggleButton.focus()\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute, executeAfterTransition, getElement, reflow } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nconst Default = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n}\n\nconst DefaultType = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n}\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n const element = this._getElement()\n if (this._config.isAnimated) {\n reflow(element)\n }\n\n element.classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n const element = this._getElement()\n this._config.rootElement.append(element)\n\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nconst Default = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n}\n\nconst DefaultType = {\n autofocus: 'boolean',\n trapElement: 'element'\n}\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return\n }\n\n if (this._config.autofocus) {\n this._config.trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n _handleFocusin(event) {\n const { trapElement } = this._config\n\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\nconst PROPERTY_PADDING = 'padding-right'\nconst PROPERTY_MARGIN = 'margin-right'\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProperty)\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty)\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty)\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty)\n return\n }\n\n Manipulator.removeDataAttribute(element, styleProperty)\n element.style.setProperty(styleProperty, value)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n return\n }\n\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel)\n }\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\nconst Default = {\n backdrop: true,\n focus: true,\n keyboard: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._isTransitioning = true\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._backdrop.show(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n this._isTransitioning = true\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())\n }\n\n dispose() {\n EventHandler.off(window, EVENT_KEY)\n EventHandler.off(this._dialog, EVENT_KEY)\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated())\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n this._triggerBackdropTransition()\n })\n\n EventHandler.on(window, EVENT_RESIZE, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog()\n }\n })\n\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return\n }\n\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n return\n }\n\n if (this._config.backdrop) {\n this.hide()\n }\n })\n })\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const initialOverflowY = this._element.style.overflowY\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden'\n }\n\n this._element.classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY\n }, this._dialog)\n }, this._dialog)\n\n this._element.focus()\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin,\n isDisabled,\n isVisible\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\nconst CLASS_NAME_HIDING = 'hiding'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOWING)\n\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate()\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.add(CLASS_NAME_HIDING)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n return\n }\n\n this.hide()\n }\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop)\n\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n })\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show()\n }\n})\n\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide()\n }\n }\n})\n\nenableDismissTrigger(Offcanvas)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n// js-docs-end allow-list\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n .some(regex => regex.test(attributeName))\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n }\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\nimport { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'\nimport { execute, getElement, isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'TemplateFactory'\n\nconst Default = {\n allowList: DefaultAllowlist,\n content: {}, // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
                                                                                                                                                                                                                                                                                                          '\n}\n\nconst DefaultType = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n}\n\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n}\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content)\n .map(config => this._resolvePossibleFunction(config))\n .filter(Boolean)\n }\n\n hasContent() {\n return this.getContent().length > 0\n }\n\n changeContent(content) {\n this._checkContent(content)\n this._config.content = { ...this._config.content, ...content }\n return this\n }\n\n toHtml() {\n const templateWrapper = document.createElement('div')\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template)\n\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector)\n }\n\n const template = templateWrapper.children[0]\n const extraClass = this._resolvePossibleFunction(this._config.extraClass)\n\n if (extraClass) {\n template.classList.add(...extraClass.split(' '))\n }\n\n return template\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config)\n this._checkContent(config.content)\n }\n\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({ selector, entry: content }, DefaultContentType)\n }\n }\n\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!templateElement) {\n return\n }\n\n content = this._resolvePossibleFunction(content)\n\n if (!content) {\n templateElement.remove()\n return\n }\n\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement)\n return\n }\n\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content)\n return\n }\n\n templateElement.textContent = content\n }\n\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this])\n }\n\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = ''\n templateElement.append(element)\n return\n }\n\n templateElement.textContent = element.textContent\n }\n}\n\nexport default TemplateFactory\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport { defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index.js'\nimport { DefaultAllowlist } from './util/sanitizer.js'\nimport TemplateFactory from './util/template-factory.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\nconst EVENT_HIDE = 'hide'\nconst EVENT_HIDDEN = 'hidden'\nconst EVENT_SHOW = 'show'\nconst EVENT_SHOWN = 'shown'\nconst EVENT_INSERTED = 'inserted'\nconst EVENT_CLICK = 'click'\nconst EVENT_FOCUSIN = 'focusin'\nconst EVENT_FOCUSOUT = 'focusout'\nconst EVENT_MOUSEENTER = 'mouseenter'\nconst EVENT_MOUSELEAVE = 'mouseleave'\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
                                                                                                                                                                                                                                                                                                          ' +\n '
                                                                                                                                                                                                                                                                                                          ' +\n '
                                                                                                                                                                                                                                                                                                          ' +\n '
                                                                                                                                                                                                                                                                                                          ',\n title: '',\n trigger: 'hover focus'\n}\n\nconst DefaultType = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n}\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)')\n }\n\n super(element, config)\n\n // Private\n this._isEnabled = true\n this._timeout = 0\n this._isHovered = null\n this._activeTrigger = {}\n this._popper = null\n this._templateFactory = null\n this._newContent = null\n\n // Protected\n this.tip = null\n\n this._setListeners()\n\n if (!this._config.selector) {\n this._fixTitle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle() {\n if (!this._isEnabled) {\n return\n }\n\n this._activeTrigger.click = !this._activeTrigger.click\n if (this._isShown()) {\n this._leave()\n return\n }\n\n this._enter()\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this._isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper()\n\n const tip = this._getTipElement()\n\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'))\n\n const { container } = this._config\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))\n }\n\n this._popper = this._createPopper(tip)\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))\n\n if (this._isHovered === false) {\n this._leave()\n }\n\n this._isHovered = false\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n hide() {\n if (!this._isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const tip = this._getTipElement()\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n this._isHovered = null // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (!this._isHovered) {\n this._disposePopper()\n }\n\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n update() {\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle())\n }\n\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())\n }\n\n return this.tip\n }\n\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml()\n\n // TODO: remove this check in v6\n if (!tip) {\n return null\n }\n\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`)\n\n const tipId = getUID(this.constructor.NAME).toString()\n\n tip.setAttribute('id', tipId)\n\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n return tip\n }\n\n setContent(content) {\n this._newContent = content\n if (this._isShown()) {\n this._disposePopper()\n this.show()\n }\n }\n\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content)\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n })\n }\n\n return this._templateFactory\n }\n\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n }\n }\n\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _isAnimated() {\n return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))\n }\n\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)\n }\n\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element])\n const attachment = AttachmentMap[placement.toUpperCase()]\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element])\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement)\n }\n }\n ]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n }\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context.toggle()\n })\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSEENTER) :\n this.constructor.eventName(EVENT_FOCUSIN)\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSELEAVE) :\n this.constructor.eventName(EVENT_FOCUSOUT)\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true\n context._enter()\n })\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =\n context._element.contains(event.relatedTarget)\n\n context._leave()\n })\n }\n }\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n\n if (!title) {\n return\n }\n\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title')\n }\n\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true\n return\n }\n\n this._isHovered = true\n\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show()\n }\n }, this._config.delay.show)\n }\n\n _leave() {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n this._isHovered = false\n\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide()\n }\n }, this._config.delay.hide)\n }\n\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout)\n this._timeout = setTimeout(handler, timeout)\n }\n\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true)\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute]\n }\n }\n\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value\n }\n }\n\n config.selector = false\n config.trigger = 'manual'\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n\n if (this.tip) {\n this.tip.remove()\n this.tip = null\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Tooltip from './tooltip.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'popover'\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\nconst Default = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
                                                                                                                                                                                                                                                                                                          ' +\n '
                                                                                                                                                                                                                                                                                                          ' +\n '

                                                                                                                                                                                                                                                                                                          ' +\n '
                                                                                                                                                                                                                                                                                                          ' +\n '
                                                                                                                                                                                                                                                                                                          ',\n trigger: 'click'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n}\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent()\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n }\n }\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_CLICK = `click${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_TARGET_LINKS = '[href]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst Default = {\n offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n}\n\nconst DefaultType = {\n offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n}\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map()\n this._observableSections = new Map()\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element\n this._activeTarget = null\n this._observer = null\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n }\n this.refresh() // initialize\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables()\n this._maybeEnableSmoothScroll()\n\n if (this._observer) {\n this._observer.disconnect()\n } else {\n this._observer = this._getNewObserver()\n }\n\n for (const section of this._observableSections.values()) {\n this._observer.observe(section)\n }\n }\n\n dispose() {\n this._observer.disconnect()\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin\n\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))\n }\n\n return config\n }\n\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK)\n\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash)\n if (observableSection) {\n event.preventDefault()\n const root = this._rootElement || window\n const height = observableSection.offsetTop - this._element.offsetTop\n if (root.scrollTo) {\n root.scrollTo({ top: height, behavior: 'smooth' })\n return\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height\n }\n })\n }\n\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n }\n\n return new IntersectionObserver(entries => this._observerCallback(entries), options)\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`)\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop\n this._process(targetElement(entry))\n }\n\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop\n this._previousScrollData.parentScrollTop = parentScrollTop\n\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null\n this._clearActiveClass(targetElement(entry))\n\n continue\n }\n\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry)\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return\n }\n\n continue\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry)\n }\n }\n }\n\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map()\n this._observableSections = new Map()\n\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target)\n\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue\n }\n\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor)\n this._observableSections.set(anchor.hash, observableSection)\n }\n }\n }\n\n _process(target) {\n if (this._activeTarget === target) {\n return\n }\n\n this._clearActiveClass(this._config.target)\n this._activeTarget = target\n target.classList.add(CLASS_NAME_ACTIVE)\n this._activateParents(target)\n\n EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })\n }\n\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n return\n }\n\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both