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
-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 youre 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
+
+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 @@
+
+
+# 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
+ Click me
+
+
+
+
+```
+
+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 @@
+
+
+# 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 @@
+
+
+# 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 @@
+
+
+# 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 @@
+
+
+# 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 @@
+
+
+# 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 @@
+
+
+# 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 @@
+
+
+# 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}}
+
+
+
+
+ {{#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 @@
+
+
+# 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 @@
+
+
+# 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 @@
+ο»Ώ
\ 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", @"m me
")]
+ 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", @"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", @"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", @"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(@"")]
+ [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", @"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, @" Reset ")]
+ [InlineData("id", @" Reset ")]
+ 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", @" 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), @" Reset ")]
+ 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(@" Reset ", html1.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Reset ", html2.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Reset ", html3.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Reset ", html4.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Reset ", html5.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Reset ", 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, @" Submit ")]
+ [InlineData("id", @"* Submit")]
+ 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", @" 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), @" Submit ")]
+ 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(@" Submit ", html1.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Submit ", html2.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Submit ", html3.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Submit ", html4.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Submit ", html5.Trim());
+ AssertExtensions.EqualWithPlaceholders(@" Submit ", 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", @" 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", @" 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", @"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" }) }, @"" },
+ { 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", @"")]
+
+ 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", @"")]
+
+ 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", @"")]
+ [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 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", @"")]
+ 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", @"")]
+ 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", @"
")]
+ [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", @"
")]
+ [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 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", @"
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(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 ControlSplitButton()
+ {
+ Size = size
+ };
+
+ // test execution
+ var html = control.Render(context, visualTree);
+
+ AssertExtensions.EqualWithPlaceholders(expected, html);
+ }
+
+ ///
+ /// Tests the backgroundcolor property of the split button control.
+ ///
+ [Theory]
+ [InlineData(TypeColorButton.Default, @" *
")]
+ [InlineData(TypeColorButton.Primary, @"
+ /// Tests the outline property of the split button control.
+ ///
+ [Theory]
+ [InlineData(false, TypeColorButton.Default, @" *
")]
+ [InlineData(true, TypeColorButton.Default, @" *
")]
+ [InlineData(true, TypeColorButton.Primary, @"
+ /// Tests the block property of the split 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 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 = @"";
+ 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" }) }, @"" },
+ { 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(@" ", 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" }) }, @"" },
+ { 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), @"")]
+ [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), @"
*")]
+ [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 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 and markup a parent is the previous sibling of any nav ancestor\n for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n item.classList.add(CLASS_NAME_ACTIVE)\n }\n }\n }\n\n _clearActiveClass(parent) {\n parent.classList.remove(CLASS_NAME_ACTIVE)\n\n const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent)\n for (const node of activeNodes) {\n node.classList.remove(CLASS_NAME_ACTIVE)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = ScrollSpy.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]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n ScrollSpy.getOrCreateInstance(spy)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(ScrollSpy)\n\nexport default ScrollSpy\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tab.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, getNextActiveElement, isDisabled } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tab'\nconst DATA_KEY = 'bs.tab'\nconst EVENT_KEY = `.${DATA_KEY}`\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}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}`\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst HOME_KEY = 'Home'\nconst END_KEY = 'End'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_DROPDOWN = 'dropdown'\n\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\nconst SELECTOR_DROPDOWN_MENU = '.dropdown-menu'\nconst NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`\n\nconst SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]'\nconst SELECTOR_OUTER = '.nav-item, .list-group-item'\nconst SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]' // TODO: could only be `tab` in v6\nconst SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`\n\nconst SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`\n\n/**\n * Class definition\n */\n\nclass Tab extends BaseComponent {\n constructor(element) {\n super(element)\n this._parent = this._element.closest(SELECTOR_TAB_PANEL)\n\n if (!this._parent) {\n return\n // TODO: should throw exception in v6\n // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n }\n\n // Set up initial aria attributes\n this._setInitialAttributes(this._parent, this._getChildren())\n\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n show() { // Shows this elem and deactivate the active sibling if exists\n const innerElem = this._element\n if (this._elemIsActive(innerElem)) {\n return\n }\n\n // Search for active tab on same parent to deactivate it\n const active = this._getActiveElem()\n\n const hideEvent = active ?\n EventHandler.trigger(active, EVENT_HIDE, { relatedTarget: innerElem }) :\n null\n\n const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { relatedTarget: active })\n\n if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {\n return\n }\n\n this._deactivate(active, innerElem)\n this._activate(innerElem, active)\n }\n\n // Private\n _activate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.add(CLASS_NAME_ACTIVE)\n\n this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.add(CLASS_NAME_SHOW)\n return\n }\n\n element.removeAttribute('tabindex')\n element.setAttribute('aria-selected', true)\n this._toggleDropDown(element, true)\n EventHandler.trigger(element, EVENT_SHOWN, {\n relatedTarget: relatedElem\n })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _deactivate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.remove(CLASS_NAME_ACTIVE)\n element.blur()\n\n this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.remove(CLASS_NAME_SHOW)\n return\n }\n\n element.setAttribute('aria-selected', false)\n element.setAttribute('tabindex', '-1')\n this._toggleDropDown(element, false)\n EventHandler.trigger(element, EVENT_HIDDEN, { relatedTarget: relatedElem })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _keydown(event) {\n if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {\n return\n }\n\n event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n event.preventDefault()\n\n const children = this._getChildren().filter(element => !isDisabled(element))\n let nextActiveElement\n\n if ([HOME_KEY, END_KEY].includes(event.key)) {\n nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]\n } else {\n const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)\n nextActiveElement = getNextActiveElement(children, event.target, isNext, true)\n }\n\n if (nextActiveElement) {\n nextActiveElement.focus({ preventScroll: true })\n Tab.getOrCreateInstance(nextActiveElement).show()\n }\n }\n\n _getChildren() { // collection of inner elements\n return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)\n }\n\n _getActiveElem() {\n return this._getChildren().find(child => this._elemIsActive(child)) || null\n }\n\n _setInitialAttributes(parent, children) {\n this._setAttributeIfNotExists(parent, 'role', 'tablist')\n\n for (const child of children) {\n this._setInitialAttributesOnChild(child)\n }\n }\n\n _setInitialAttributesOnChild(child) {\n child = this._getInnerElement(child)\n const isActive = this._elemIsActive(child)\n const outerElem = this._getOuterElement(child)\n child.setAttribute('aria-selected', isActive)\n\n if (outerElem !== child) {\n this._setAttributeIfNotExists(outerElem, 'role', 'presentation')\n }\n\n if (!isActive) {\n child.setAttribute('tabindex', '-1')\n }\n\n this._setAttributeIfNotExists(child, 'role', 'tab')\n\n // set attributes to the related panel too\n this._setInitialAttributesOnTargetPanel(child)\n }\n\n _setInitialAttributesOnTargetPanel(child) {\n const target = SelectorEngine.getElementFromSelector(child)\n\n if (!target) {\n return\n }\n\n this._setAttributeIfNotExists(target, 'role', 'tabpanel')\n\n if (child.id) {\n this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)\n }\n }\n\n _toggleDropDown(element, open) {\n const outerElem = this._getOuterElement(element)\n if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n return\n }\n\n const toggle = (selector, className) => {\n const element = SelectorEngine.findOne(selector, outerElem)\n if (element) {\n element.classList.toggle(className, open)\n }\n }\n\n toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)\n toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)\n outerElem.setAttribute('aria-expanded', open)\n }\n\n _setAttributeIfNotExists(element, attribute, value) {\n if (!element.hasAttribute(attribute)) {\n element.setAttribute(attribute, value)\n }\n }\n\n _elemIsActive(elem) {\n return elem.classList.contains(CLASS_NAME_ACTIVE)\n }\n\n // Try to get the inner element (usually the .nav-link)\n _getInnerElement(elem) {\n return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)\n }\n\n // Try to get the outer element (usually the .nav-item)\n _getOuterElement(elem) {\n return elem.closest(SELECTOR_OUTER) || elem\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tab.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]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n Tab.getOrCreateInstance(this).show()\n})\n\n/**\n * Initialize on focus\n */\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n Tab.getOrCreateInstance(element)\n }\n})\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tab)\n\nexport default Tab\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap toast.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, reflow } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'toast'\nconst DATA_KEY = 'bs.toast'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\n\nconst DefaultType = {\n animation: 'boolean',\n autohide: 'boolean',\n delay: 'number'\n}\n\nconst Default = {\n animation: true,\n autohide: true,\n delay: 5000\n}\n\n/**\n * Class definition\n */\n\nclass Toast extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._timeout = null\n this._hasMouseInteraction = false\n this._hasKeyboardInteraction = false\n this._setListeners()\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() {\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._clearTimeout()\n\n if (this._config.animation) {\n this._element.classList.add(CLASS_NAME_FADE)\n }\n\n const complete = () => {\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN)\n\n this._maybeScheduleHide()\n }\n\n this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated\n reflow(this._element)\n this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)\n\n this._queueCallback(complete, this._element, this._config.animation)\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 const complete = () => {\n this._element.classList.add(CLASS_NAME_HIDE) // @deprecated\n this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.classList.add(CLASS_NAME_SHOWING)\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n dispose() {\n this._clearTimeout()\n\n if (this.isShown()) {\n this._element.classList.remove(CLASS_NAME_SHOW)\n }\n\n super.dispose()\n }\n\n isShown() {\n return this._element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n\n _maybeScheduleHide() {\n if (!this._config.autohide) {\n return\n }\n\n if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n return\n }\n\n this._timeout = setTimeout(() => {\n this.hide()\n }, this._config.delay)\n }\n\n _onInteraction(event, isInteracting) {\n switch (event.type) {\n case 'mouseover':\n case 'mouseout': {\n this._hasMouseInteraction = isInteracting\n break\n }\n\n case 'focusin':\n case 'focusout': {\n this._hasKeyboardInteraction = isInteracting\n break\n }\n\n default: {\n break\n }\n }\n\n if (isInteracting) {\n this._clearTimeout()\n return\n }\n\n const nextElement = event.relatedTarget\n if (this._element === nextElement || this._element.contains(nextElement)) {\n return\n }\n\n this._maybeScheduleHide()\n }\n\n _setListeners() {\n EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))\n EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))\n }\n\n _clearTimeout() {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Toast.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](this)\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Toast)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Toast)\n\nexport default Toast\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap index.umd.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Alert from './src/alert.js'\nimport Button from './src/button.js'\nimport Carousel from './src/carousel.js'\nimport Collapse from './src/collapse.js'\nimport Dropdown from './src/dropdown.js'\nimport Modal from './src/modal.js'\nimport Offcanvas from './src/offcanvas.js'\nimport Popover from './src/popover.js'\nimport ScrollSpy from './src/scrollspy.js'\nimport Tab from './src/tab.js'\nimport Toast from './src/toast.js'\nimport Tooltip from './src/tooltip.js'\n\nexport default {\n Alert,\n Button,\n Carousel,\n Collapse,\n Dropdown,\n Modal,\n Offcanvas,\n Popover,\n ScrollSpy,\n Tab,\n Toast,\n Tooltip\n}\n"],"mappings":"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"}
\ No newline at end of file
+{"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","map","sel","join","SelectorEngine","concat","Element","findOne","children","child","matches","parents","ancestor","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","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","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","dd","div","dl","dt","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.3'\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 !== '#' ? hrefAttribute.trim() : null\n }\n\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null\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 {\n execute, executeAfterTransition, getElement, reflow\n} 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 {\n defineJQueryPlugin, isRTL, isVisible, reflow\n} 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 dd: [],\n div: [],\n dl: [],\n dt: [],\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 {\n defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop\n} 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 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 {\n defineJQueryPlugin, getElement, isDisabled, isVisible\n} 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 and markup a parent is the previous sibling of any nav ancestor\n for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n item.classList.add(CLASS_NAME_ACTIVE)\n }\n }\n }\n\n _clearActiveClass(parent) {\n parent.classList.remove(CLASS_NAME_ACTIVE)\n\n const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent)\n for (const node of activeNodes) {\n node.classList.remove(CLASS_NAME_ACTIVE)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = ScrollSpy.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]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n ScrollSpy.getOrCreateInstance(spy)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(ScrollSpy)\n\nexport default ScrollSpy\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap tab.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, getNextActiveElement, isDisabled } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tab'\nconst DATA_KEY = 'bs.tab'\nconst EVENT_KEY = `.${DATA_KEY}`\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}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}`\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst HOME_KEY = 'Home'\nconst END_KEY = 'End'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_DROPDOWN = 'dropdown'\n\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\nconst SELECTOR_DROPDOWN_MENU = '.dropdown-menu'\nconst NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`\n\nconst SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]'\nconst SELECTOR_OUTER = '.nav-item, .list-group-item'\nconst SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]' // TODO: could only be `tab` in v6\nconst SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`\n\nconst SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`\n\n/**\n * Class definition\n */\n\nclass Tab extends BaseComponent {\n constructor(element) {\n super(element)\n this._parent = this._element.closest(SELECTOR_TAB_PANEL)\n\n if (!this._parent) {\n return\n // TODO: should throw exception in v6\n // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n }\n\n // Set up initial aria attributes\n this._setInitialAttributes(this._parent, this._getChildren())\n\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n show() { // Shows this elem and deactivate the active sibling if exists\n const innerElem = this._element\n if (this._elemIsActive(innerElem)) {\n return\n }\n\n // Search for active tab on same parent to deactivate it\n const active = this._getActiveElem()\n\n const hideEvent = active ?\n EventHandler.trigger(active, EVENT_HIDE, { relatedTarget: innerElem }) :\n null\n\n const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { relatedTarget: active })\n\n if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {\n return\n }\n\n this._deactivate(active, innerElem)\n this._activate(innerElem, active)\n }\n\n // Private\n _activate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.add(CLASS_NAME_ACTIVE)\n\n this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.add(CLASS_NAME_SHOW)\n return\n }\n\n element.removeAttribute('tabindex')\n element.setAttribute('aria-selected', true)\n this._toggleDropDown(element, true)\n EventHandler.trigger(element, EVENT_SHOWN, {\n relatedTarget: relatedElem\n })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _deactivate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.remove(CLASS_NAME_ACTIVE)\n element.blur()\n\n this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.remove(CLASS_NAME_SHOW)\n return\n }\n\n element.setAttribute('aria-selected', false)\n element.setAttribute('tabindex', '-1')\n this._toggleDropDown(element, false)\n EventHandler.trigger(element, EVENT_HIDDEN, { relatedTarget: relatedElem })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _keydown(event) {\n if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {\n return\n }\n\n event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n event.preventDefault()\n\n const children = this._getChildren().filter(element => !isDisabled(element))\n let nextActiveElement\n\n if ([HOME_KEY, END_KEY].includes(event.key)) {\n nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]\n } else {\n const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)\n nextActiveElement = getNextActiveElement(children, event.target, isNext, true)\n }\n\n if (nextActiveElement) {\n nextActiveElement.focus({ preventScroll: true })\n Tab.getOrCreateInstance(nextActiveElement).show()\n }\n }\n\n _getChildren() { // collection of inner elements\n return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)\n }\n\n _getActiveElem() {\n return this._getChildren().find(child => this._elemIsActive(child)) || null\n }\n\n _setInitialAttributes(parent, children) {\n this._setAttributeIfNotExists(parent, 'role', 'tablist')\n\n for (const child of children) {\n this._setInitialAttributesOnChild(child)\n }\n }\n\n _setInitialAttributesOnChild(child) {\n child = this._getInnerElement(child)\n const isActive = this._elemIsActive(child)\n const outerElem = this._getOuterElement(child)\n child.setAttribute('aria-selected', isActive)\n\n if (outerElem !== child) {\n this._setAttributeIfNotExists(outerElem, 'role', 'presentation')\n }\n\n if (!isActive) {\n child.setAttribute('tabindex', '-1')\n }\n\n this._setAttributeIfNotExists(child, 'role', 'tab')\n\n // set attributes to the related panel too\n this._setInitialAttributesOnTargetPanel(child)\n }\n\n _setInitialAttributesOnTargetPanel(child) {\n const target = SelectorEngine.getElementFromSelector(child)\n\n if (!target) {\n return\n }\n\n this._setAttributeIfNotExists(target, 'role', 'tabpanel')\n\n if (child.id) {\n this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)\n }\n }\n\n _toggleDropDown(element, open) {\n const outerElem = this._getOuterElement(element)\n if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n return\n }\n\n const toggle = (selector, className) => {\n const element = SelectorEngine.findOne(selector, outerElem)\n if (element) {\n element.classList.toggle(className, open)\n }\n }\n\n toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)\n toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)\n outerElem.setAttribute('aria-expanded', open)\n }\n\n _setAttributeIfNotExists(element, attribute, value) {\n if (!element.hasAttribute(attribute)) {\n element.setAttribute(attribute, value)\n }\n }\n\n _elemIsActive(elem) {\n return elem.classList.contains(CLASS_NAME_ACTIVE)\n }\n\n // Try to get the inner element (usually the .nav-link)\n _getInnerElement(elem) {\n return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)\n }\n\n // Try to get the outer element (usually the .nav-item)\n _getOuterElement(elem) {\n return elem.closest(SELECTOR_OUTER) || elem\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tab.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]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n Tab.getOrCreateInstance(this).show()\n})\n\n/**\n * Initialize on focus\n */\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n Tab.getOrCreateInstance(element)\n }\n})\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tab)\n\nexport default Tab\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap toast.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, reflow } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'toast'\nconst DATA_KEY = 'bs.toast'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\n\nconst DefaultType = {\n animation: 'boolean',\n autohide: 'boolean',\n delay: 'number'\n}\n\nconst Default = {\n animation: true,\n autohide: true,\n delay: 5000\n}\n\n/**\n * Class definition\n */\n\nclass Toast extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._timeout = null\n this._hasMouseInteraction = false\n this._hasKeyboardInteraction = false\n this._setListeners()\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() {\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._clearTimeout()\n\n if (this._config.animation) {\n this._element.classList.add(CLASS_NAME_FADE)\n }\n\n const complete = () => {\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN)\n\n this._maybeScheduleHide()\n }\n\n this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated\n reflow(this._element)\n this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)\n\n this._queueCallback(complete, this._element, this._config.animation)\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 const complete = () => {\n this._element.classList.add(CLASS_NAME_HIDE) // @deprecated\n this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.classList.add(CLASS_NAME_SHOWING)\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n dispose() {\n this._clearTimeout()\n\n if (this.isShown()) {\n this._element.classList.remove(CLASS_NAME_SHOW)\n }\n\n super.dispose()\n }\n\n isShown() {\n return this._element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n\n _maybeScheduleHide() {\n if (!this._config.autohide) {\n return\n }\n\n if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n return\n }\n\n this._timeout = setTimeout(() => {\n this.hide()\n }, this._config.delay)\n }\n\n _onInteraction(event, isInteracting) {\n switch (event.type) {\n case 'mouseover':\n case 'mouseout': {\n this._hasMouseInteraction = isInteracting\n break\n }\n\n case 'focusin':\n case 'focusout': {\n this._hasKeyboardInteraction = isInteracting\n break\n }\n\n default: {\n break\n }\n }\n\n if (isInteracting) {\n this._clearTimeout()\n return\n }\n\n const nextElement = event.relatedTarget\n if (this._element === nextElement || this._element.contains(nextElement)) {\n return\n }\n\n this._maybeScheduleHide()\n }\n\n _setListeners() {\n EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))\n EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))\n }\n\n _clearTimeout() {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Toast.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](this)\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Toast)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Toast)\n\nexport default Toast\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap index.umd.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Alert from './src/alert.js'\nimport Button from './src/button.js'\nimport Carousel from './src/carousel.js'\nimport Collapse from './src/collapse.js'\nimport Dropdown from './src/dropdown.js'\nimport Modal from './src/modal.js'\nimport Offcanvas from './src/offcanvas.js'\nimport Popover from './src/popover.js'\nimport ScrollSpy from './src/scrollspy.js'\nimport Tab from './src/tab.js'\nimport Toast from './src/toast.js'\nimport Tooltip from './src/tooltip.js'\n\nexport default {\n Alert,\n Button,\n Carousel,\n Collapse,\n Dropdown,\n Modal,\n Offcanvas,\n Popover,\n ScrollSpy,\n Tab,\n Toast,\n Tooltip\n}\n"],"mappings":"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"}
\ No newline at end of file
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.button.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.button.js
new file mode 100644
index 00000000..1dc3c5d8
--- /dev/null
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.button.js
@@ -0,0 +1,82 @@
+/**
+ * A simple button.
+ * The following events are triggered:
+ * - webexpress.webui.click
+ */
+webexpress.webui.buttonCtrl = class extends webexpress.webui.events {
+ _button = $(" ");
+
+ /**
+ * Constructor
+ * @param settings Options for styling the control:
+ * - id Sets the id of the control.
+ * - css The CSS classes used to design the control.
+ * - label The text.
+ * - icon The Icon class of the control.
+ * - color The color of the control text.
+ * - disabled Indicates whether the control is disabled.
+ * - url The URL the button points to.
+ * - onclick The click event handler for the button.
+ */
+ constructor(settings) {
+ super();
+
+ const id = settings.id;
+ const css = settings.css;
+ const label = settings.label || "";
+ const icon = settings.icon;
+ const color = settings.color;
+ const disabled = settings.disabled ?? false;
+ const url = settings.url;
+ const onclick = settings.onclick;
+
+ // Create the button icon and text
+ const buttonIcon = $("").addClass(icon);
+ const buttonText = $("").text(label);
+
+ let buttonContent;
+
+ if (icon) {
+ buttonContent = $("").append(buttonIcon).append(buttonText);
+ }
+ else {
+ buttonContent = $("
").append(buttonText);
+ }
+
+ if (url) {
+ buttonContent = $("
").attr("href", url).append(buttonContent);
+ }
+
+ this._button.append(buttonContent);
+ this._button.attr("id", id || "");
+
+ if (css) {
+ this._button.addClass(css);
+ }
+
+ if (color) {
+ this._button.addClass(color);
+ }
+
+ if (disabled) {
+ this._button.attr("disabled", "disabled");
+ }
+
+ if (onclick) {
+ const func = Function("sender", onclick);
+ this._button.click((e) => { func(this._button); });
+ }
+
+ // Trigger click event
+ this._button.click(() => {
+ this.trigger('webexpress.webui.click');
+ });
+ }
+
+ /**
+ * Returns the control.
+ */
+ get getCtrl() {
+ return this._button;
+ }
+}
\ No newline at end of file
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.dropdownbutton.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.dropdownbutton.js
new file mode 100644
index 00000000..e14cc601
--- /dev/null
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.dropdownbutton.js
@@ -0,0 +1,107 @@
+/**
+ * A dropdown button that offers advanced features (links).
+ */
+webexpress.webui.dropDownButtonCtrl = class {
+ _container = $("
");
+
+ /**
+ * Constructor
+ * @param options The menu items array of { css: "", icon: "", color: "", label: "", url: "", onclick: "", item: null, disabled: false}.
+ * @param settings Options for styling the control:
+ * - id Sets the id of the control.
+ * - css The CSS classes used to design the control.
+ * - menucss A CSS class for designing the pop-up menu.
+ * - label The text.
+ * - icon The Icon class of the control.
+ */
+ constructor(options, settings) {
+ const id = settings.id;
+ const css = settings.css;
+ const menuCSS = settings.menucss;
+ const label = settings.label || "";
+ const icon = settings.icon || "fas fa-ellipsis-h";
+
+ const button = $("" + label + " ");
+ const ul = $("");
+
+ if (menuCSS) {
+ ul.addClass(menuCSS);
+ }
+
+ for (const option of options) {
+ const label = option.label;
+ const css = option.css || "dropdown-item";
+ const icon = option.icon;
+ const color = option.color;
+ const item = option.item;
+ const disabled = option.disabled ?? false;
+ const url = option.url || "#";
+ const onclick = option.onclick;
+
+ const li = this.createMenuItem(option, css, icon, color, item, disabled, url, onclick);
+ ul.append(li);
+ }
+
+ this._container.attr("id", id || "");
+ if (css) {
+ this._container.addClass(css);
+ }
+
+ this._container.append(button);
+ this._container.append(ul);
+ }
+
+ /**
+ * Creates a menu item.
+ * @param {Object} option - The option for the menu item.
+ * @param {string} css - The CSS class of the menu item.
+ * @param {string} icon - The icon class of the menu item.
+ * @param {string} color - The color of the menu item.
+ * @param {Object} item - The associated element.
+ * @param {boolean} disabled - Indicates whether the menu item is disabled.
+ * @param {string} url - The URL of the menu item.
+ * @param {Function} onclick - The click event handler.
+ * @returns {jQuery} The created menu item.
+ */
+ createMenuItem(option, css, icon, color, item, disabled, url, onclick) {
+ const li = $(" ").addClass(css);
+
+ if (css === "dropdown-item") {
+ if (!disabled) {
+ const a = $(" ");
+ if (icon) {
+ a.append($("
"));
+ }
+ if (onclick) {
+ const func = Function("option", "item", onclick);
+ a.click((e) => { func(option, item); });
+ }
+ a.append($("
" + option.label + " "));
+ li.append(a);
+ } else {
+ const p = $("
");
+ if (icon) {
+ p.append($("
"));
+ }
+ p.append(option.label);
+ li.append(p).addClass("disabled");
+ }
+ } else if (css === "dropdown-header") {
+ if (icon) {
+ li.append($("
"));
+ }
+ li.append($("
" + option.label + " "));
+ } else if (css === "dropdown-divider") {
+ // Divider
+ }
+
+ return li;
+ }
+
+ /**
+ * Returns the control.
+ */
+ get getCtrl() {
+ return this._container;
+ }
+}
\ No newline at end of file
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.expand.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.expand.js
index ae260f1e..f30d6d2b 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.expand.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.expand.js
@@ -6,7 +6,7 @@
webexpress.webui.expandCtrl = class extends webexpress.webui.events {
_container = $("
");
_content = $("
");
- _expandicon = $(" ");
+ _expandicon = $(" ");
_expand = true;
/**
@@ -15,63 +15,66 @@ webexpress.webui.expandCtrl = class extends webexpress.webui.events {
* - id Sets the id of the control.
* - css The CSS classes used to design the control.
* - header The headline text.
+ * - headerCss The CSS classes used to design the header.
*/
constructor(settings) {
- let id = settings.id;
- let css = settings.css;
- let header = settings.header != null ? settings.header : "";
-
- let expandheader = $("" + header + " ");
-
+ const id = settings.id;
+ const css = settings.css;
+ const header = settings.header ?? "";
+ const headerCss = settings.headerCss ?? "text-primary";
+
+ const expandheader = $("").addClass(headerCss).attr("aria-label", header).text(header);
+
super();
-
- this._container.attr("id", id ?? "");
-
- this._expandicon.click(function () {
+
+ this._container.attr("id", id || "");
+
+ // Toggle expand state on click of expand icon or header
+ this._expandicon.click(() => {
this.expand = !this.expand;
- }.bind(this));
-
- expandheader.click(function () {
+ });
+
+ expandheader.click(() => {
this.expand = !this.expand;
- }.bind(this));
+ });
this._container.append(this._expandicon);
this._container.append(expandheader);
this._container.append(this._content);
-
+
this.expand = true;
}
-
+
/**
* Update of the control.
*/
update() {
this._content.toggleClass("hide");
}
-
+
/**
- * Determines if the control is pasted on.
+ * Determines if the control is expanded.
*/
get expand() {
return this._expand;
}
-
+
/**
* Show or hide the control content.
* @param value A value that determines whether the control is expanded or closed.
*/
set expand(value) {
- if (this._expand != value) {
+ if (this._expand !== value) {
this.trigger('webexpress.webui.change.visibility', value);
this._expand = value;
}
-
+
if (this._expand) {
this._content.removeClass("hide");
- this._expandicon.addClass("expand-angle-down");
+ this._expandicon.addClass("wx-expand-angle-down");
} else {
this._content.addClass("hide");
- this._expandicon.removeClass("expand-angle-down");
+ this._expandicon.removeClass("wx-expand-angle-down");
}
}
@@ -81,17 +84,16 @@ webexpress.webui.expandCtrl = class extends webexpress.webui.events {
get content() {
return this._content.children();
}
-
+
/**
* Sets the content.
* @param content An array of contents.
*/
set content(content) {
this._content.children().remove();
-
this._content.append(content);
}
-
+
/**
* Returns the control.
*/
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.form.progress.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.form.progress.js
index 7db45d98..496f6d9b 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.form.progress.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.form.progress.js
@@ -12,7 +12,7 @@ webexpress.webui.form.progess = class {
*/
constructor(formId, method) {
- const form = $('#' + formId);
+ /* const form = $('#' + formId);
const progress = form.find('.progress');
const progressBar = progress.children('.progress-bar');
@@ -39,6 +39,6 @@ webexpress.webui.form.progess = class {
};
xhr.send(new FormData(form.get(0)));
- });
+ });*/
}
}
\ No newline at end of file
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.modalformular.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.modalform.js
similarity index 94%
rename from src/WebExpress.WebUI/Assets/js/webexpress.webui.modalformular.js
rename to src/WebExpress.WebUI/Assets/js/webexpress.webui.modalform.js
index f7dd2d9d..ce85b37d 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.modalformular.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.modalform.js
@@ -1,11 +1,11 @@
/**
- * A modular formular.
+ * A modular form.
* The following events are triggered:
* - webexpress.webui.close
*/
-webexpress.webui.modalFormularCtrl = class extends webexpress.webui.events {
+webexpress.webui.modalFormCtrl = class extends webexpress.webui.events {
_uri = null;
- _container = $("
");
+ _container = $("
");
/**
* Constructor
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.more.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.more.js
index e431ac53..4f308031 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.more.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.more.js
@@ -15,86 +15,87 @@ webexpress.webui.moreCtrl = class {
* - icon The Icon class of the control.
*/
constructor(options, settings) {
- let id = settings.id;
- let css = settings.css;
- let menuCSS = settings.menucss;
- let label = settings.label != null ? settings.label : "";
- let icon = settings.icon != null ? settings.icon : "fas fa-ellipsis-h";
+ const id = settings.id;
+ const css = settings.css;
+ const menuCSS = settings.menucss;
+ const label = settings.label || "";
+ const icon = settings.icon || "fas fa-ellipsis-h";
- let button = $("" + label + " ");
- let ul = $("");
+ const button = $("" + label + " ");
+ const ul = $("");
- if (menuCSS != null) {
+ if (menuCSS) {
ul.addClass(menuCSS);
}
- options.forEach(function (option) {
- let label = option.label;
- let css = option.css ?? "dropdown-item";
- let icon = option.icon;
- let color = option.color;
- let item = option.item;
- let disabled = option.disabled ?? "return false";
-
- let url = option.url != null ? option.url : "#";
- let onclick = option.onclick;
+ for (const option of options) {
+ const label = option.label;
+ const css = option.css || "dropdown-item";
+ const icon = option.icon;
+ const color = option.color;
+ const item = option.item;
+ const disabled = option.disabled ?? false;
+ const url = option.url || "#";
+ const onclick = option.onclick;
- let disabledFunction = Function("item", disabled == true ? "return true;" : disabled == false ? "return false" : disabled);
- disabled = disabledFunction(item) ?? false;
+ const li = this.createMenuItem(option, css, icon, color, item, disabled, url, onclick);
+ ul.append(li);
+ }
- let li = $(" ");
+ this._container.attr("id", id || "");
+ if (css) {
+ this._container.addClass(css);
+ }
- li.addClass(css);
+ this._container.append(button);
+ this._container.append(ul);
+ }
+
+ /**
+ * Creates a menu item.
+ * @param {Object} option - The option for the menu item.
+ * @param {string} css - The CSS class of the menu item.
+ * @param {string} icon - The icon class of the menu item.
+ * @param {string} color - The color of the menu item.
+ * @param {Object} item - The associated element.
+ * @param {boolean} disabled - Indicates whether the menu item is disabled.
+ * @param {string} url - The URL of the menu item.
+ * @param {Function} onclick - The click event handler.
+ * @returns {jQuery} The created menu item.
+ */
+ createMenuItem(option, css, icon, color, item, disabled, url, onclick) {
+ const li = $(" ").addClass(css);
- if (css == "dropdown-item") {
- if (disabled == false) {
- let a = $(" ");
- if (icon != null) {
- let span = $(" ");
- a.append(span);
- }
- if (css != null) {
- li.addClass(css);
- }
- if (onclick != null) {
- let func = Function("option", "item", onclick);
- a.click(function (e) { func(option, item); });
- }
- a.append($("" + label + " "));
- li.append(a);
- } else {
- let p = $(" ");
- if (icon != null) {
- let span = $(" ");
- p.append(span);
- }
- p.append(label);
- li.append(p);
- li.addClass("disabled")
+ if (css === "dropdown-item") {
+ if (!disabled) {
+ const a = $(" ");
+ if (icon) {
+ a.append($(" "));
}
- }
- else if (css == "dropdown-header") {
- if (icon != null) {
- let span = $(" ");
- li.append(span);
+ if (onclick) {
+ const func = Function("option", "item", onclick);
+ a.click((e) => { func(option, item); });
}
-
- li.append($("" + label + " "));
+ a.append($("" + option.label + " "));
+ li.append(a);
+ } else {
+ const p = $(" ");
+ if (icon) {
+ p.append($(" "));
+ }
+ p.append(option.label);
+ li.append(p).addClass("disabled");
}
- else if (css == "dropdown-divider") {
-
+ } else if (css === "dropdown-header") {
+ if (icon) {
+ li.append($(" "));
}
-
- ul.append(li);
- });
-
- this._container.attr("id", id ?? "");
- if (css != null) {
- this._container.addClass(css);
+ li.append($("" + option.label + " "));
+ } else if (css === "dropdown-divider") {
+ // Divider
}
- this._container.append(button);
- this._container.append(ul);
+ return li;
}
/**
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.move.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.move.js
index d77d9b91..fa8056a4 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.move.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.move.js
@@ -1,10 +1,10 @@
/**
- * Selection box to enable options.
+ * A selection box to enable options.
* The following events are triggered:
* - webexpress.webui.change.value with parameter value.
*/
webexpress.webui.moveCtrl = class extends webexpress.webui.events {
- _container = $("
");
+ _container = $("
");
_selectedList = $("");
_availableList = $("");
_buttonToSelectedAll = $(" ");
@@ -23,30 +23,26 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
* - id Sets the id of the control.
* - name The control name.
* - css The CSS classes used to design the control.
- * - header The heading { Selected, Available }.
+ * - header The heading { selected, available }.
* - buttons The button label { toselectedall, toselected, toavailable, toavailableall }.
*/
constructor(settings) {
super();
- let id = settings.id;
- let name = settings.name;
- let css = settings.css;
- let header = settings.header;
- let buttons = settings.buttons;
- let selectedContainer = $("
");
- let selectedHeader = $("" + header.selected + " ");
- let availableContainer = $("
");
- let availableHeader = $("" + header.available + " ");
- let buttonContainer = $("
");
+ const { id, name, css, header, buttons } = settings;
+ const selectedContainer = $("
");
+ const selectedHeader = $("" + header.selected + " ");
+ const availableContainer = $("
");
+ const availableHeader = $("" + header.available + " ");
+ const buttonContainer = $("
");
this._container.attr("id", id ?? "");
- if (css != null) {
+ if (css) {
this._container.addClass(css);
}
- if (name != null) {
+ if (name) {
this._hidden.attr("name", name);
}
@@ -64,67 +60,68 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
buttonContainer.append(this._buttonToAvailable);
buttonContainer.append(this._buttonToAvailableAll);
- selectedContainer.on('dragenter', function(e) {
+ // Event listeners for drag and drop functionality
+ selectedContainer.on('dragenter', (e) => {
e.preventDefault();
this._selectedList.addClass('drag-over');
- }.bind(this));
+ });
- selectedContainer.on('dragover', function(e) {
+ selectedContainer.on('dragover', (e) => {
e.preventDefault();
this._selectedList.addClass('drag-over');
- }.bind(this));
+ });
- selectedContainer.on('dragleave', function() {
+ selectedContainer.on('dragleave', () => {
this._selectedList.removeClass('drag-over');
- }.bind(this));
+ });
- selectedContainer.on('drop', function(e) {
+ selectedContainer.on('drop', (e) => {
this._selectedList.removeClass('drag-over');
this.moveToSelected();
e.preventDefault();
- }.bind(this));
+ });
- availableContainer.on('dragenter', function(e) {
+ availableContainer.on('dragenter', (e) => {
e.preventDefault();
this._availableList.addClass('drag-over');
- }.bind(this));
+ });
- availableContainer.on('dragover', function(e) {
+ availableContainer.on('dragover', (e) => {
e.preventDefault();
this._availableList.addClass('drag-over');
- }.bind(this));
+ });
- availableContainer.on('dragleave', function() {
+ availableContainer.on('dragleave', () => {
this._availableList.removeClass('drag-over');
- }.bind(this));
+ });
- availableContainer.on('drop', function(e) {
+ availableContainer.on('drop', (e) => {
this._availableList.removeClass('drag-over');
this.moveToAvailable();
e.preventDefault();
- }.bind(this));
+ });
- this._buttonToSelectedAll.click(function() {
+ this._buttonToSelectedAll.click(() => {
this.moveToSelectedAll();
- }.bind(this));
+ });
- this._buttonToSelected.click(function() {
+ this._buttonToSelected.click(() => {
this.moveToSelected();
- }.bind(this));
+ });
- this._buttonToAvailableAll.click(function() {
+ this._buttonToAvailableAll.click(() => {
this.moveToAvailableAll();
- }.bind(this));
+ });
- this._buttonToAvailable.click(function() {
+ this._buttonToAvailable.click(() => {
this.moveToAvailable();
- }.bind(this));
+ });
this._container.append(selectedContainer);
this._container.append(buttonContainer);
this._container.append(availableContainer);
- if (name !== undefined && name != null) {
+ if (name) {
this._container.append(this._hidden);
}
@@ -136,16 +133,14 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
*/
moveToSelectedAll() {
this.value = this._options.map(element => element.Id);
-
this.update();
}
/**
- * Moves a single entry to the left (selected).
+ * Moves selected entries to the left (selected).
*/
moveToSelected() {
- this.value = this._values.concat(Array.from(this._availableoptions.values()).filter(elem => elem != null).map(elem => elem.Id));
-
+ this.value = this._values.concat(Array.from(this._availableoptions.entries()).filter(([key, value]) => value != null).map(([key, value]) => value.Id));
this.update();
}
@@ -154,16 +149,14 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
*/
moveToAvailableAll() {
this.value = [];
-
this.update();
}
/**
- * Moves a single entry to the right (available).
+ * Moves selected entries to the right (available).
*/
moveToAvailable() {
- this.value = this._values.filter(b => !Array.from(this._selectedoptions.values()).filter(elem => elem != null).map(elem => elem.Id).includes(b));
-
+ this.value = this._values.filter(b => !Array.from(this._selectedoptions.entries()).filter(([key, value]) => value != null).map(([key, value]) => value.Id).includes(b));
this.update();
}
@@ -171,18 +164,18 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
* Update of the control.
*/
update() {
- let values = this._values != null ? this._values : [];
- let comparison = (a, b) => a === b.Id;
- let relativeComplement = this._options.filter(b => values.every(a => !comparison(a, b)));
- let intersection = this._options.filter(b => values.includes(b.Id));
+ const values = this._values != null ? this._values : [];
+ const comparison = (a, b) => a === b.Id;
+ const relativeComplement = this._options.filter(b => values.every(a => !comparison(a, b)));
+ const intersection = this._options.filter(b => values.includes(b.Id));
this._selectedList.children().remove();
this._availableList.children().remove();
this._selectedoptions.clear();
this._availableoptions.clear();
- function updateselection() {
- this._selectedoptions.forEach(function(value, key) {
+ const updateselection = () => {
+ this._selectedoptions.forEach((value, key) => {
if (value != null) {
key.addClass("bg-primary");
key.children().addClass("text-white");
@@ -191,7 +184,7 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
key.children().removeClass("text-white");
}
});
- this._availableoptions.forEach(function(value, key) {
+ this._availableoptions.forEach((value, key) => {
if (value != null) {
key.addClass("bg-primary");
key.children().addClass("text-white");
@@ -201,7 +194,7 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
}
});
- if (Array.from(this._availableoptions.values()).filter(elem => elem != null).length == 0) {
+ if (Array.from(this._availableoptions.values()).filter(elem => elem != null).length === 0) {
this._buttonToSelected.addClass("disabled");
this._buttonToSelected.prop("disabled", true);
} else {
@@ -209,20 +202,20 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._buttonToSelected.prop("disabled", false);
}
- if (Array.from(this._selectedoptions.values()).filter(elem => elem != null).length == 0) {
+ if (Array.from(this._selectedoptions.values()).filter(elem => elem != null).length === 0) {
this._buttonToAvailable.addClass("disabled");
this._buttonToAvailable.prop("disabled", true);
} else {
this._buttonToAvailable.removeClass("disabled");
this._buttonToAvailable.prop("disabled", false);
}
- }
+ };
- intersection.forEach(function(currentValue) {
- let li = $(" ");
- let img = $(" ");
- let icon = $(" ");
- let a = $("" + "".concat(currentValue.label) + " ");
+ intersection.forEach((currentValue) => {
+ const li = $(" ");
+ const img = $(" ");
+ const icon = $(" ");
+ const a = $("" + currentValue.label + " ");
if (currentValue.icon != null) {
li.append(icon);
}
@@ -232,7 +225,7 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
li.append(a);
this._selectedoptions.set(li, null);
- li.click(function() {
+ li.click((event) => {
if (event.ctrlKey) {
if (!Array.from(this._selectedoptions.values()).some(elem => elem === currentValue)) {
this._selectedoptions.set(li, currentValue);
@@ -245,14 +238,14 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._selectedoptions.set(li, currentValue);
this._availableoptions.forEach((value, key, map) => map.set(key, null));
}
- updateselection.call(this);
- }.bind(this, a)).dblclick(function() {
+ updateselection();
+ }).dblclick(() => {
this._selectedoptions.forEach((value, key, map) => map.set(key, null));
this._selectedoptions.set(li, currentValue);
this._availableoptions.forEach((value, key, map) => map.set(key, null));
this.moveToAvailable();
- }.bind(this, a)).keyup(function() {
+ }).keyup((event) => {
if (event.keyCode === 32) {
if (!Array.from(this._selectedoptions.keys()).some(elem => elem === currentValue)) {
this._selectedoptions.set(li, currentValue);
@@ -260,25 +253,25 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._selectedoptions.set(li, null);
}
this._availableoptions.forEach((value, key, map) => map.set(key, null));
- updateselection.call(this);
+ updateselection();
}
- }.bind(this, a));
+ });
- li.on('dragstart', function dragStart(e) {
+ li.on('dragstart', (e) => {
this._selectedoptions.forEach((value, key, map) => map.set(key, null));
this._selectedoptions.set(li, currentValue);
this._availableoptions.forEach((value, key, map) => map.set(key, null));
- updateselection.call(this);
- }.bind(this));
+ updateselection();
+ });
this._selectedList.append(li);
- }.bind(this));
+ });
- relativeComplement.forEach(function(currentValue) {
- let li = $(" ");
- let img = $(" ");
- let icon = $(" ");
- let a = $("" + "".concat(currentValue.label) + " ");
+ relativeComplement.forEach((currentValue) => {
+ const li = $(" ");
+ const img = $(" ");
+ const icon = $(" ");
+ const a = $("" + currentValue.label + " ");
if (currentValue.icon != null) {
li.append(icon);
}
@@ -288,7 +281,7 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
li.append(a);
this._availableoptions.set(li, null);
- li.click(function() {
+ li.click((event) => {
if (event.ctrlKey) {
if (!Array.from(this._availableoptions.values()).some(elem => elem === currentValue)) {
this._availableoptions.set(li, currentValue);
@@ -302,14 +295,14 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._availableoptions.set(li, currentValue);
}
- updateselection.call(this);
- }.bind(this, a)).dblclick(function() {
+ updateselection();
+ }).dblclick(() => {
this._selectedoptions.forEach((value, key, map) => map.set(key, null));
this._availableoptions.forEach((value, key, map) => map.set(key, null));
this._availableoptions.set(li, currentValue);
this.moveToSelected();
- }.bind(this, a)).keyup(function() {
+ }).keyup((event) => {
if (event.keyCode === 32) {
if (!Array.from(this._availableoptions.keys()).some(elem => elem === currentValue)) {
this._availableoptions.set(li, currentValue);
@@ -317,21 +310,21 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._availableoptions.set(li, null);
}
this._selectedoptions.forEach((value, key, map) => map.set(key, null));
- updateselection.call(this);
+ updateselection();
}
- }.bind(this, a));
+ });
- li.on('dragstart', function dragStart(e) {
+ li.on('dragstart', (e) => {
this._selectedoptions.forEach((value, key, map) => map.set(key, null));
this._availableoptions.forEach((value, key, map) => map.set(key, null));
this._availableoptions.set(li, currentValue);
- updateselection.call(this);
- }.bind(this));
+ updateselection();
+ });
this._availableList.append(li);
- }.bind(this));
+ });
- if (relativeComplement.length == 0) {
+ if (relativeComplement.length === 0) {
this._buttonToSelectedAll.addClass("disabled");
this._buttonToSelectedAll.prop("disabled", true);
} else {
@@ -339,7 +332,7 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._buttonToSelectedAll.prop("disabled", false);
}
- if (values.length == 0) {
+ if (values.length === 0) {
this._buttonToAvailableAll.addClass("disabled");
this._buttonToAvailableAll.prop("disabled", true);
} else {
@@ -347,7 +340,7 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
this._buttonToAvailableAll.prop("disabled", false);
}
- updateselection.call(this);
+ updateselection();
}
/**
@@ -363,7 +356,6 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
*/
set options(options) {
this._options = options;
-
this.update();
}
@@ -379,13 +371,10 @@ webexpress.webui.moveCtrl = class extends webexpress.webui.events {
* @param values An array with object ids.
*/
set value(values) {
- if (this._values != values) {
-
+ if (this._values !== values) {
this._values = values;
- this._hidden.val(this._values.map(element => element).join(';'));
-
+ this._hidden.val(this._values.join(';'));
this.update();
-
this.trigger('webexpress.webui.change.value', values);
}
}
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.search.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.search.js
index 6b64a04d..574c95fa 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.search.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.search.js
@@ -4,7 +4,7 @@
* - webexpress.webui.change.filter with parameter filter.
*/
webexpress.webui.searchCtrl = class extends webexpress.webui.events {
- _container = $("");
+ _container = $("");
/**
* Constructor
@@ -16,33 +16,38 @@ webexpress.webui.searchCtrl = class extends webexpress.webui.events {
*/
constructor(settings) {
super();
-
- let id = settings.id;
- let css = settings.css;
- let placeholder = settings.placeholder != null ? settings.placeholder : "";
- let icon = settings.icon != null ? settings.icon : "fas fa-search";
- let searchicon = $(" ");
- let searchinput = $(" ");
- let searchappend = $("");
+ const id = settings.id;
+ const css = settings.css;
+ const placeholder = settings.placeholder ?? "";
+ const icon = settings.icon ?? "fas fa-search";
+
+ const searchIcon = $(" ");
+ const searchInput = $(" ");
+ const searchClear = $("");
- this._container.attr("id", id ?? "");
+ this._container.attr("id", id || "");
- searchinput.keyup(function () {
- this.trigger('webexpress.webui.change.filter', searchinput.val());
-
- }.bind(this));
+ // Trigger filter change event on keyup
+ searchInput.keyup(() => {
+ this.trigger('webexpress.webui.change.filter', searchInput.val());
+ });
- searchappend.click(function () {
- searchinput.val('');
+ // Clear the input and trigger filter change event on click
+ searchClear.click(() => {
+ searchInput.val('');
this.trigger('webexpress.webui.change.filter', '');
- }.bind(this));
+ });
- this._container.addClass(css);
+ // Add CSS classes if provided
+ if (css) {
+ this._container.addClass(css);
+ }
- this._container.append(searchicon);
- this._container.append(searchinput);
- this._container.append(searchappend);
+ // Append elements to the container
+ this._container.append(searchIcon);
+ this._container.append(searchInput);
+ this._container.append(searchClear);
}
/**
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.selection.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.selection.js
index 30e8a5bf..70fe8001 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.selection.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.selection.js
@@ -5,7 +5,7 @@
* - webexpress.webui.change.value with parameter value.
*/
webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
- _container = $(" ");
+ _container = $(" ");
_selection = $("");
_hidden = $(" ");
_dropdownmenu = $("");
@@ -16,7 +16,7 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
_placeholder = null;
_hidedescription = false;
_multiselect = false;
- _optionfilter = function (x, y) { return x?.toLowerCase().startsWith(y?.toLowerCase()); };
+ _optionfilter = (x, y) => x?.toLowerCase().startsWith(y?.toLowerCase());
/**
* Constructor
@@ -28,18 +28,18 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
* - multiselect Allows you to select multiple items.
*/
constructor(settings) {
- let id = settings.id;
- let name = settings.name;
- let css = settings.css;
- let hidedescription = settings.hidedescription;
- let multiselect = settings.multiselect;
- let placeholder = settings.placeholder !== undefined ? settings.placeholder : null;
-
- let dropdown = $(" ");
- let expand = $(" ");
-
super();
+ const id = settings.id;
+ const name = settings.name;
+ const css = settings.css;
+ const hidedescription = settings.hidedescription;
+ const multiselect = settings.multiselect;
+ const placeholder = settings.placeholder !== undefined ? settings.placeholder : null;
+
+ const dropdown = $(" ");
+ const expand = $(" ");
+
this._container.attr("id", id ?? "");
if (css != null) {
@@ -58,25 +58,25 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
this._multiselect = multiselect;
}
- this._container.on('show.bs.dropdown', function () {
- let width = this._container.width();
+ this._container.on('show.bs.dropdown', () => {
+ const width = this._container.width();
this._dropdownmenu.width(width);
- }.bind(this));
+ });
- this._container.on('shown.bs.dropdown', function () {
+ this._container.on('shown.bs.dropdown', () => {
this._filter.focus();
this.update();
- }.bind(this));
-
- this._filter.keyup(function (e) {
- let filter = this._filter.val();
+ });
+
+ this._filter.keyup((e) => {
+ const filter = this._filter.val();
e.stopPropagation();
this.trigger('webexpress.webui.change.filter', filter !== undefined || filter != null ? filter : "");
this.update();
if (this._dropdownmenu.is(":hidden")) {
dropdown.dropdown('toggle');
}
- }.bind(this));
+ });
this._placeholder = placeholder;
@@ -99,82 +99,82 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
update() {
this._dropdownoptions.children().remove();
- this._options.forEach(function (option) {
- let id = option.id ?? null;
- let label = option.label ?? null;
+ this._options.forEach((option) => {
+ const id = option.id ?? null;
+ const label = option.label ?? null;
if (!this._values.includes(id)) {
if (id == null && (label == null || label == '-')) {
- let li = $(" ");
+ const li = $(" ");
this._dropdownoptions.append(li);
} else if (id == null && label != null) {
- let li = $("");
+ const li = $("");
this._dropdownoptions.append(li);
} else {
- let description = option.description != null && option.description.length > 0 ? option.description : null;
- let image = option.image ?? null;
- let color = option.color ?? 'text-dark';
- let instruction = option.instruction != null ? "(" + option.instruction + ") ": "";
- let li = $(" ");
- let a = $("" + option.label + " " + instruction);
- let p = $("" + description + "
");
+ const description = option.description != null && option.description.length > 0 ? option.description : null;
+ const image = option.image ?? null;
+ const color = option.color ?? 'text-dark';
+ const instruction = option.instruction != null ? "(" + option.instruction + ") ": "";
+ const li = $(" ");
+ const a = $("" + option.label + " " + instruction);
+ const p = $("" + description + "
");
if (image != null) {
- let box = $(" ");
- let span = $(" ");
- let img = $(" ");
+ const box = $(" ");
+ const span = $(" ");
+ const img = $(" ");
box.append(img);
box.append(a);
span.append(box);
- if (this._hidedescription == false && description != null) {
+ if (!this._hidedescription && description != null) {
span.append(p);
}
li.append(span);
} else {
li.append(a);
- if (this._hidedescription && description != null) {
+ if (!this._hidedescription && description != null) {
li.append(p);
}
}
- li.click(function () {
+ li.click(() => {
if (!this._multiselect) {
this.value = [];
}
if (!this._values.includes(option.id)) {
- let value = this.value.slice();
+ const value = this.value.slice();
value.push(option.id);
this.value = value;
this._filter.val("");
}
this.update();
- }.bind(this));
+ });
if (this._optionfilter(option.label, this._filter.val())) {
this._dropdownoptions.append(li);
}
}
}
- }.bind(this));
+ });
this._selection.children("li").remove();
- this._values.forEach(function (value) {
- let option = this._options.find(elem => elem.id == value);
+ this._values.forEach((value) => {
+ const option = this._options.find(elem => elem.id == value);
if (option != null) {
- let label = option.label ?? null;
- let image = option.image ?? null;
- let color = option.color ?? 'text-dark';
- let a = $("" + option.label + " ");
- let close = $(" ");
- let li = $(" ");
-
- close.click(function () {
+ const label = option.label ?? null;
+ const image = option.image ?? null;
+ const color = option.color ?? 'text-dark';
+ const a = $("" + option.label + " ");
+ const close = $(" ");
+ const li = $(" ");
+
+ close.click(() => {
this.value = this._values.filter(item => item !== value);
- }.bind(this));
+ });
if (image != null) {
- let img = $(" ");
+ const img = $(" ");
li.append(img);
li.append(a);
li.append(close);
@@ -185,7 +185,7 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
this._selection.append(li);
}
}
- }.bind(this));
+ });
}
/**
@@ -204,7 +204,7 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
// determine selected options
let selectedOptions = this.selectedOptions;
// remove the selected options if they are included in the new options
- selectedOptions = selectedOptions.filter(elem => !options.some(o => o.Id === elem.id));
+ selectedOptions = selectedOptions.filter(elem => !options.some(o => o.id === elem.id));
// join the selected and the new options
this._options = [...new Set([...options, ...selectedOptions])];
} else {
@@ -233,12 +233,9 @@ webexpress.webui.selectionCtrl = class extends webexpress.webui.events {
*/
set value(values) {
if (this._values != values) {
-
this._values = values;
-
this.update();
- this._hidden.val(this._values.map(element => element).join(';'));
-
+ this._hidden.val(this._values.join(';'));
this.trigger('webexpress.webui.change.value', values);
}
}
diff --git a/src/WebExpress.WebUI/Assets/js/webexpress.webui.tree.js b/src/WebExpress.WebUI/Assets/js/webexpress.webui.tree.js
index 628e0bba..9357a137 100644
--- a/src/WebExpress.WebUI/Assets/js/webexpress.webui.tree.js
+++ b/src/WebExpress.WebUI/Assets/js/webexpress.webui.tree.js
@@ -4,7 +4,7 @@
* - webexpress.webui.change.visibility
*/
webexpress.webui.treeCtrl = class extends webexpress.webui.events {
- _container = $("");
+ _container = $("");
_nodes = [];
/**
@@ -16,13 +16,13 @@ webexpress.webui.treeCtrl = class extends webexpress.webui.events {
constructor(settings) {
super();
- let id = settings.id ?? "";
- let css = settings.css;
+ const id = settings.id ?? "";
+ const css = settings.css;
this._container.attr("id", id);
this._container.addClass(css);
}
-
+
/**
* Update of the control.
* This function works recursively.
@@ -30,39 +30,40 @@ webexpress.webui.treeCtrl = class extends webexpress.webui.events {
* @param nodes The nodes to be inserted.
*/
update(container, nodes) {
+ // Clear existing children
container.children().remove();
- nodes.forEach(function (node) {
- let children = node.children;
- let color = node.color ?? "";
- let li = $(" ");
- let expand = $(" ");
- let icon = $(" ");
+ nodes.forEach((node) => {
+ const children = node.children;
+ const color = node.color ?? "";
+ const li = $(" ");
+ const expand = $(" ");
+ const icon = $(" ");
li.append(expand);
if (children != null) {
- let ul = $("");
- let indicator = $(" ");
+ const ul = $("");
+ const indicator = $(" ");
if (node.expand) {
this.update(ul, children);
- indicator.addClass("tree-expand");
+ indicator.addClass("wx-tree-expand");
}
expand.append(indicator);
li.append(ul);
- expand.click(function () {
+ expand.click(() => {
this.trigger('webexpress.webui.change.visibility', li, node);
if (!node.expand) {
- indicator.addClass("tree-expand");
+ indicator.addClass("wx-tree-expand");
this.update(ul, children);
} else {
- indicator.removeClass("tree-expand");
+ indicator.removeClass("wx-tree-expand");
ul.children().remove();
}
node.expand = !node.expand;
- }.bind(this));
+ });
} else {
- let indicator = $(" ");
+ const indicator = $(" ");
expand.append(indicator);
}
@@ -70,36 +71,33 @@ webexpress.webui.treeCtrl = class extends webexpress.webui.events {
expand.append(icon);
}
- if (node.render != null && (typeof node.render === 'string' || node.render instanceof String)) {
- let render = Function("node", node.render);
- let renderResult = render(node);
- if (renderResult != null && renderResult != null) {
+ if (typeof node.render === 'string' || node.render instanceof String) {
+ const render = new Function("node", node.render);
+ const renderResult = render(node);
+ if (renderResult != null) {
expand.append(renderResult);
}
} else {
-
expand.append($("" + node.label + " "));
}
container.append(li);
-
- }.bind(this));
+ });
}
-
+
/**
* Returns the tree nodes.
*/
get nodes() {
return this._nodes;
}
-
+
/**
* Set the nodes of the tree.
* @param nodes An array with object ids {id, label, description, image, color, expand}.
*/
set nodes(nodes) {
this._nodes = nodes;
-
this.update(this._container, this._nodes);
}
diff --git a/src/WebExpress.WebUI/Internationalization/de b/src/WebExpress.WebUI/Internationalization/de
index 934ee590..87b6ec98 100644
--- a/src/WebExpress.WebUI/Internationalization/de
+++ b/src/WebExpress.WebUI/Internationalization/de
@@ -2,20 +2,11 @@
plugin.name=WebExpress.WebUI
plugin.description=Stellt Basiskomponenten bereit.
-module.name=WebExpress.WebUI
-module.description=Stellt Basiskomponenten bereit.
-
-fragmentmanager.initialization=Der Fragmentmanager wurde initialisiert.
-fragmentmanager.register=Das Fragment '{0}' wurde in der Sektion '{1}' der Anwendung '{2}' registriert.
-fragmentmanager.error.section=Die Angabe der Sektion ist fehlerhaft.
-fragmentmanager.wrongtype=Der Typ '{0}' implementiert die Schnittstelle '{1}' nicht.
-fragmentmanager.moduleless=Das Fragment '{0}' aus dem Plugin '{1}' besitzt keine Angaben zum Modul.
-fragmentmanager.addfragment.duplicate=Das Fragment '{0}' des Moduls '{1}' ist bereits hinzugefΓΌgt worden.
-fragmentmanager.titel=Fragmente:
-fragmentmanager.fragment={0}
+notificationmanager.initialization=Der Benachrichtigungsmanager wurde initialisiert.
form.empty.id=Das Formular verfΓΌgt ΓΌber keine Id!
form.submit.label=Speichern
+form.reset.label=ZurΓΌcksetzen
form.next.label=Speichern und weiter
form.cancel.label=Abbrechen
form.selectionmove.available=VerfΓΌgbare Optionen
diff --git a/src/WebExpress.WebUI/Internationalization/en b/src/WebExpress.WebUI/Internationalization/en
index 03a30a76..b46eb5b9 100644
--- a/src/WebExpress.WebUI/Internationalization/en
+++ b/src/WebExpress.WebUI/Internationalization/en
@@ -2,20 +2,11 @@
plugin.name=WebExpress.WebUI
plugin.description=Provides basic components.
-module.name=WebExpress.WebUI
-module.description=Provides basic components.
-
-fragmentmanager.initialization=The fragment manager has been initialized.
-fragmentmanager.register=The fragment '{0}' has been registered in the '{1}' section of application '{2}'.
-fragmentmanager.error.section=The section is incorrect.
-fragmentmanager.wrongtype=The type '{0}' does not implement the interface '{1}'.
-fragmentmanager.moduleless=The fragment '{0}' from the plugin '{1}' has no information about the module.
-fragmentmanager.addfragment.duplicate=The fragment '{0}' of module '{1}' has already been added.
-fragmentmanager.titel=Fragments:
-fragmentmanager.fragment={0}
+notificationmanager.initialization=The notification manager has been initialized.
form.empty.id=The form does not have an id!
form.submit.label=Submit
+form.reset.label=Reset
form.next.label=Next
form.cancel.label=Cancel
form.selectionmove.available=Available options
diff --git a/src/WebExpress.WebUI/Module.cs b/src/WebExpress.WebUI/Module.cs
deleted file mode 100644
index 05342628..00000000
--- a/src/WebExpress.WebUI/Module.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-ο»Ώusing WebExpress.WebCore.WebAttribute;
-using WebExpress.WebCore.WebModule;
-
-namespace WebExpress.WebUI
-{
- [Application("*")]
- [Name("module.name")]
- [Description("module.description")]
- [Icon("/assets/img/Logo.png")]
- [AssetPath("/")]
- [ContextPath("/modules/wxui")]
- public sealed class Module : IModule
- {
- ///
- /// Constructor
- ///
- public Module()
- {
- }
-
- ///
- /// Instillation of the module. Here, for example, managed resources can be loaded.
- ///
- /// The context that applies to the execution of the plugin.
- public void Initialization(IModuleContext context)
- {
-
- }
-
- ///
- /// Invoked when the module starts working. The call is concurrent.
- ///
- public void Run()
- {
-
- }
-
- ///
- /// Release unmanaged resources that have been reserved during use.
- ///
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/Plugin.cs b/src/WebExpress.WebUI/Plugin.cs
deleted file mode 100644
index c2a3206b..00000000
--- a/src/WebExpress.WebUI/Plugin.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-ο»Ώusing WebExpress.WebCore.WebAttribute;
-using WebExpress.WebCore.WebPlugin;
-
-[assembly: SystemPlugin()]
-
-namespace WebExpress.WebUI
-{
- [Name("webexpress.webui")]
- [Description("plugin.description")]
- [Icon("/assets/img/Logo.png")]
- public sealed class Plugin : IPlugin
- {
- ///
- /// Constructor
- ///
- public Plugin()
- {
- }
-
- ///
- /// Initialization of the plugin. Here, for example, managed resources can be loaded.
- ///
- /// The context of the plugin that applies to the execution of the plugin.
- public void Initialization(IPluginContext context)
- {
- }
-
- ///
- /// Called when the plugin starts working. Run is called concurrently.
- ///
- public void Run()
- {
-
- }
-
- ///
- /// Release of unmanaged resources reserved during use.
- ///
- public void Dispose()
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/IComponentAttribute.cs b/src/WebExpress.WebUI/WebAttribute/IComponentAttribute.cs
deleted file mode 100644
index d324ccdd..00000000
--- a/src/WebExpress.WebUI/WebAttribute/IComponentAttribute.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-ο»Ώnamespace WebExpress.WebUI.WebAttribute
-{
- ///
- /// Kennzeichnet eine Klasse als Plugin-Komponente
- ///
- public interface IFragmentAttribute
- {
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/OptionAttribute.cs b/src/WebExpress.WebUI/WebAttribute/OptionAttribute.cs
deleted file mode 100644
index aa0ae11e..00000000
--- a/src/WebExpress.WebUI/WebAttribute/OptionAttribute.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-ο»Ώusing System;
-using WebExpress.WebCore.WebAttribute;
-
-namespace WebExpress.WebUI.WebAttribute
-{
- ///
- /// Activation of options (e.g. WebEx.WebApp.Setting.SystemInformation for the display of system information).
- ///
- [AttributeUsage(AttributeTargets.Class)]
- public class OptionAttribute : System.Attribute, IResourceAttribute
- {
- ///
- /// Constructor
- ///
- /// The option to activate.
- public OptionAttribute(string option)
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/OrderAttribute.cs b/src/WebExpress.WebUI/WebAttribute/OrderAttribute.cs
deleted file mode 100644
index 7fbbeeef..00000000
--- a/src/WebExpress.WebUI/WebAttribute/OrderAttribute.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-ο»Ώnamespace WebExpress.WebUI.WebAttribute
-{
- ///
- /// Attribute used to identify a class as a plugin component.
- ///
- public class OrderAttribute : System.Attribute, IFragmentAttribute
- {
- ///
- /// Constructor
- ///
- /// The order within the section.
- public OrderAttribute(int order)
- {
- }
-
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SectionAttribute.cs b/src/WebExpress.WebUI/WebAttribute/SectionAttribute.cs
deleted file mode 100644
index d41c042a..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SectionAttribute.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-ο»Ώnamespace WebExpress.WebUI.WebAttribute
-{
- ///
- /// Attribute to identify a section.
- ///
- public class SectionAttribute : System.Attribute, IFragmentAttribute
- {
- ///
- /// Constructor
- ///
- /// The section where the component is embedded.
- public SectionAttribute(string section)
- {
- }
-
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SettingContextAttribute.cs b/src/WebExpress.WebUI/WebAttribute/SettingContextAttribute.cs
deleted file mode 100644
index 7b7a747a..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SettingContextAttribute.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-ο»Ώusing System;
-using WebExpress.WebCore.WebAttribute;
-
-namespace WebExpress.WebUI.WebAttribute
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class SettingContextAttribute : Attribute, IResourceAttribute
- {
- ///
- /// Constructor
- ///
- /// The context in which the settings page is associated.
- public SettingContextAttribute(string context)
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SettingGroupAttribute.cs b/src/WebExpress.WebUI/WebAttribute/SettingGroupAttribute.cs
deleted file mode 100644
index 02e1b11d..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SettingGroupAttribute.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-ο»Ώusing System;
-using WebExpress.WebCore.WebAttribute;
-
-namespace WebExpress.WebUI.WebAttribute
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class SettingGroupAttribute : System.Attribute, IResourceAttribute
- {
- ///
- /// Constructor
- ///
- /// The group name.
- public SettingGroupAttribute(string name)
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SettingHideAttribute.cs b/src/WebExpress.WebUI/WebAttribute/SettingHideAttribute.cs
deleted file mode 100644
index d374cfb0..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SettingHideAttribute.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-ο»Ώusing WebExpress.WebCore.WebAttribute;
-
-namespace WebExpress.WebUI.WebAttribute
-{
- public class SettingHideAttribute : System.Attribute, IResourceAttribute
- {
- ///
- /// Constructor
- ///
- public SettingHideAttribute()
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SettingIconAttribute.cs b/src/WebExpress.WebUI/WebAttribute/SettingIconAttribute.cs
deleted file mode 100644
index 1be9ba84..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SettingIconAttribute.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-ο»Ώusing WebExpress.WebUI.WebControl;
-using WebExpress.WebCore.WebAttribute;
-
-namespace WebExpress.WebUI.WebAttribute
-{
- public class SettingIconAttribute : System.Attribute, IResourceAttribute
- {
- ///
- /// Constructor
- ///
- /// The icon.
- public SettingIconAttribute(TypeIcon icon)
- {
-
- }
-
- ///
- /// Constructor
- ///
- /// The icon.
- public SettingIconAttribute(string icon)
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SettingSection.cs b/src/WebExpress.WebUI/WebAttribute/SettingSection.cs
deleted file mode 100644
index feb60892..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SettingSection.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-ο»Ώnamespace WebExpress.WebUI.WebAttribute
-{
- ///
- /// Definition of keys for the identification of sections in Wen pages, which can be occupied by components.
- ///
- public enum SettingSection
- {
- ///
- /// Returns the preference section.
- ///
- Preferences,
-
- ///
- /// Returns the primary section.
- ///
- Primary,
-
- ///
- /// Returns the secondary section.
- ///
- Secondary
- }
-}
diff --git a/src/WebExpress.WebUI/WebAttribute/SettingSectionAttribute.cs b/src/WebExpress.WebUI/WebAttribute/SettingSectionAttribute.cs
deleted file mode 100644
index 58e88a05..00000000
--- a/src/WebExpress.WebUI/WebAttribute/SettingSectionAttribute.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-ο»Ώusing System;
-using WebExpress.WebCore.WebAttribute;
-
-namespace WebExpress.WebUI.WebAttribute
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class SettingSectionAttribute : System.Attribute, IResourceAttribute
- {
- ///
- /// Constructor
- ///
- /// The section where the settings page is listed.
- public SettingSectionAttribute(SettingSection section)
- {
-
- }
- }
-}
diff --git a/src/WebExpress.WebUI/WebControl/Control.cs b/src/WebExpress.WebUI/WebControl/Control.cs
index 4d1a30e2..f09d9d74 100644
--- a/src/WebExpress.WebUI/WebControl/Control.cs
+++ b/src/WebExpress.WebUI/WebControl/Control.cs
@@ -3,10 +3,13 @@
using System.Linq;
using System.Runtime.CompilerServices;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Abstract base class for all controls.
+ ///
public abstract class Control : IControl
{
///
@@ -99,6 +102,15 @@ public virtual TypeHeight Height
set => SetProperty(value, () => value.ToClass());
}
+ ///
+ /// Returns or sets the flex grow property of the control.
+ ///
+ public virtual TypeFlexGrow FlexGrow
+ {
+ get => (TypeFlexGrow)GetProperty(TypeFlexGrow.None);
+ set => SetProperty(value, () => value.ToClass());
+ }
+
///
/// Returns or sets the id of the control.
///
@@ -107,17 +119,17 @@ public virtual TypeHeight Height
///
/// Returns or sets the css class.
///
- public List Classes { get; set; } = new List();
+ public IEnumerable Classes { get; set; } = [];
///
- /// Retruns or sets properties determined by enums.
+ /// Returns or sets the css style.
///
- protected Dictionary, Func>> Propertys { get; private set; } = new Dictionary, Func>>();
+ public IEnumerable Styles { get; set; } = [];
///
- /// Returns or sets the css style.
+ /// Returns or sets properties determined by enums.
///
- public List Styles { get; set; } = new List();
+ protected Dictionary, Func>> Propertys { get; private set; } = [];
///
/// Returns or sets the role.
@@ -135,19 +147,12 @@ public virtual TypeHeight Height
public bool Enable { get; set; } = true;
///
- /// Convert to html.
+ /// Initializes a new instance of the class.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public abstract IHtmlNode Render(RenderContext context);
-
- ///
- /// Constructor
- ///
- /// The id.
+ /// The control id.
public Control(string id = null)
{
- Id = id;
+ Id = id?.Replace('.', '-');
HorizontalAlignment = TypeHorizontalAlignment.Default;
BackgroundColor = new PropertyColorBackground(TypeColorBackground.Default);
@@ -193,7 +198,7 @@ protected Enum GetProperty([CallerMemberName] string propertyName = "")
///
/// Returns a property.
///
- /// he name of the property.
+ /// The name of the property.
/// The value.
protected IProperty GetPropertyObject([CallerMemberName] string propertyName = "")
{
@@ -299,5 +304,13 @@ protected string GetStyles()
return string.Join(" ", Styles.Union(list));
}
+
+ ///
+ /// Converts the control to an HTML representation.
+ ///
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public abstract IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree);
}
}
diff --git a/src/WebExpress.WebUI/WebControl/ControlArlert.cs b/src/WebExpress.WebUI/WebControl/ControlArlert.cs
index 164959a6..10d4b812 100644
--- a/src/WebExpress.WebUI/WebControl/ControlArlert.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlArlert.cs
@@ -1,5 +1,5 @@
ο»Ώusing WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
@@ -46,7 +46,7 @@ public TypeFade Fade
public string Text { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlAlert(string id = null)
@@ -55,11 +55,12 @@ public ControlAlert(string id = null)
}
///
- /// Convert to html.
+ /// Converts the control to an HTML representation.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
var head = new HtmlElementTextSemanticsStrong
(
diff --git a/src/WebExpress.WebUI/WebControl/ControlAttribute.cs b/src/WebExpress.WebUI/WebControl/ControlAttribute.cs
index f26ffe48..0da62117 100644
--- a/src/WebExpress.WebUI/WebControl/ControlAttribute.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlAttribute.cs
@@ -2,24 +2,25 @@
using System.Linq;
using WebExpress.WebCore.Internationalization;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebCore.WebIcon;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
///
- /// Display of a name-value pair.
+ /// Represents a control attribute with a name-value pair.
///
public class ControlAttribute : Control
{
///
- /// Returns or sets the text.farbe des Namens
+ /// Returns or sets the text color of the name.
///
public PropertyColorText NameColor { get; set; }
///
/// Returns or sets the icon.
///
- public PropertyIcon Icon { get; set; }
+ public IIcon Icon { get; set; }
///
/// Returns or sets the name.
@@ -37,7 +38,7 @@ public class ControlAttribute : Control
public Uri Uri { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlAttribute(string id = null)
@@ -46,29 +47,27 @@ public ControlAttribute(string id = null)
}
///
- /// Convert to html.
+ /// Converts the control to an HTML representation.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
if (!Enable)
{
return null;
}
- var icon = new HtmlElementTextSemanticsSpan()
- {
- Class = Icon?.ToClass()
- };
+ var icon = Icon?.Render(renderContext, visualTree);
- var name = new HtmlElementTextSemanticsSpan(new HtmlText(context.I18N(Name)))
+ var name = new HtmlElementTextSemanticsSpan(new HtmlText(I18N.Translate(renderContext.Request?.Culture, Name)))
{
Id = string.IsNullOrWhiteSpace(Id) ? string.Empty : $"{Id}_name",
Class = NameColor?.ToClass()
};
- var value = new HtmlElementTextSemanticsSpan(new HtmlText(context.I18N(Value)))
+ var value = new HtmlElementTextSemanticsSpan(new HtmlText(I18N.Translate(renderContext.Request?.Culture, Value)))
{
Id = string.IsNullOrWhiteSpace(Id) ? string.Empty : $"{Id}_value",
Class = NameColor?.ToClass()
@@ -76,7 +75,7 @@ public override IHtmlNode Render(RenderContext context)
var html = new HtmlElementTextContentDiv
(
- Icon != null && Icon.HasIcon ? icon : null,
+ Icon != null ? icon : null,
name,
Uri != null ? new HtmlElementTextSemanticsA(value) { Href = Uri.ToString() } : value
)
diff --git a/src/WebExpress.WebUI/WebControl/ControlAvatar.cs b/src/WebExpress.WebUI/WebControl/ControlAvatar.cs
index f21b6aca..9180697f 100644
--- a/src/WebExpress.WebUI/WebControl/ControlAvatar.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlAvatar.cs
@@ -1,10 +1,13 @@
ο»Ώusing System;
using System.Linq;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a control that displays a user's avatar, which can include an image or initials, and optionally a modal dialog.
+ ///
public class ControlAvatar : Control
{
///
@@ -32,28 +35,21 @@ public TypeSizeButton Size
public ControlModal Modal { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlAvatar(string id = null)
: base(id)
{
- Init();
}
///
- /// Initialization
+ /// Converts the control to an HTML representation.
///
- private void Init()
- {
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
var img = default(HtmlElement);
@@ -69,14 +65,14 @@ public override IHtmlNode Render(RenderContext context)
img = new HtmlElementTextSemanticsB(new HtmlText(i))
{
- Class = "bg-info text-light"
+ Class = Css.Concatenate("bg-info text-light")
};
}
var html = new HtmlElementTextContentDiv(img, new HtmlText(User))
{
Id = Id,
- Class = Css.Concatenate("profile", GetClasses()),
+ Class = Css.Concatenate("wx-profile", GetClasses()),
Style = GetStyles(),
Role = Role
};
@@ -86,7 +82,7 @@ public override IHtmlNode Render(RenderContext context)
html.AddUserAttribute("data-bs-toggle", "modal");
html.AddUserAttribute("data-bs-target", "#" + Modal.Id);
- return new HtmlList(html, Modal.Render(context));
+ return new HtmlList(html, Modal.Render(renderContext, visualTree));
}
return html;
diff --git a/src/WebExpress.WebUI/WebControl/ControlBadge.cs b/src/WebExpress.WebUI/WebControl/ControlBadge.cs
index 2db07e4c..e018018b 100644
--- a/src/WebExpress.WebUI/WebControl/ControlBadge.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlBadge.cs
@@ -1,11 +1,11 @@
ο»Ώusing System;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
///
- /// A numerical indicator.
+ /// Represents a badge control that can display a numerical indicator.
///
public class ControlBadge : Control
{
@@ -35,7 +35,7 @@ public TypePillBadge Pill
///
/// Returns or sets the value.
///
- public string Value { get; set; }
+ public int? Value { get; set; }
///
/// Return or specifies the vertical orientation..
@@ -56,61 +56,28 @@ public PropertySizeText Size
}
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlBadge(string id = null)
: base(id)
{
- Init();
}
///
- /// Constructor
+ /// Converts the control to an HTML representation.
///
- /// The id of the control.
- /// The value.
- public ControlBadge(string id, string value)
- : base(id)
- {
- Value = value;
-
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The id.
- /// The value.
- public ControlBadge(string id, int value)
- : base(id)
- {
- Value = value.ToString();
-
- Init();
- }
-
- ///
- /// Initialization
- ///
- private void Init()
- {
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
if (Uri != null)
{
return new HtmlElementTextSemanticsA(new HtmlText(Value.ToString()))
{
Id = Id,
- Class = Css.Concatenate("badge", GetClasses()),
+ Class = Css.Concatenate("wx-badge", GetClasses()),
Style = GetStyles(),
Href = Uri.ToString(),
Role = Role
@@ -120,7 +87,7 @@ public override IHtmlNode Render(RenderContext context)
return new HtmlElementTextSemanticsSpan(new HtmlText(Value.ToString()))
{
Id = Id,
- Class = Css.Concatenate("badge", GetClasses()),
+ Class = Css.Concatenate("wx-badge", GetClasses()),
Style = GetStyles(),
Role = Role
};
diff --git a/src/WebExpress.WebUI/WebControl/ControlBreadcrumb.cs b/src/WebExpress.WebUI/WebControl/ControlBreadcrumb.cs
index 80e8276a..ddc38b5f 100644
--- a/src/WebExpress.WebUI/WebControl/ControlBreadcrumb.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlBreadcrumb.cs
@@ -1,18 +1,25 @@
-ο»Ώusing WebExpress.WebCore.Internationalization;
+ο»Ώusing System;
+using System.Linq;
+using WebExpress.WebCore.Internationalization;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebCore.WebUri;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a breadcrumb control that displays a list of links indicating the current
+ /// page's location within a navigational hierarchy.
+ ///
public class ControlBreadcrumb : Control
{
///
/// Return or sets the uri.
///
- public string Uri { get; set; }
+ public IUri Uri { get; set; }
///
- /// Return or sets the root element.
+ /// Returns or sets the name to display when the breadcrumb is empty.
///
public string EmptyName { get; set; }
@@ -36,57 +43,40 @@ public TypeSizeButton Size
public ushort TakeLast { get; set; } = ushort.MaxValue;
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlBreadcrumb(string id = null)
: base(id)
- {
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The id of the control.
- /// Der Verzeichnispfad
- public ControlBreadcrumb(string id, string uri)
- : base(id)
- {
- Uri = uri;
- }
-
- ///
- /// Initialization
- ///
- private void Init()
{
Size = TypeSizeButton.Small;
BackgroundColor = new PropertyColorBackground(TypeColorBackground.Light);
}
///
- /// Convert to html.
+ /// Converts the control to an HTML representation.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
var html = new HtmlElementTextContentOl()
{
+ Id = Id,
Class = Css.Concatenate("breadcrumb bg-light ps-2", GetClasses()),
Style = GetStyles(),
};
if (!string.IsNullOrWhiteSpace(Prefix))
{
- html.Elements.Add
+ html.Add
(
new HtmlElementTextContentLi
(
new HtmlElementTextContentDiv
(
- new HtmlText(InternationalizationManager.I18N(context.Culture, Prefix))
+ new HtmlText(I18N.Translate(renderContext.Request?.Culture, Prefix))
)
{
Class = "me-2 text-muted"
@@ -95,24 +85,31 @@ public override IHtmlNode Render(RenderContext context)
);
}
- foreach (var part in context.Uri.PathSegments)
+ if (Uri == null)
+ {
+ return html;
+ }
+
+ var takeLast = Math.Min(TakeLast, Uri.PathSegments.Count());
+ var from = Uri.PathSegments.Count() - takeLast;
+
+ for (int i = from + 1; i < Uri.PathSegments.Count() + 1; i++)
{
- if (part.Display != null)
+ var path = Uri.Take(i);
+
+ if (path.Display != null)
{
- var display = part.GetDisplay(context.Culture);
- var href = part.ToString();
+ var display = I18N.Translate(renderContext.Request?.Culture, path.Display);
+ var href = path.ToString();
- html.Elements.Add
+ html.Add
(
new HtmlElementTextContentLi
(
- //new ControlIcon(Page)
- //{
- // Icon = path.Icon
- //}.ToHtml(),
new HtmlElementTextSemanticsA(display)
{
- Href = href
+ Href = href,
+ Class = "link"
}
)
{
@@ -122,40 +119,6 @@ public override IHtmlNode Render(RenderContext context)
}
}
- //var takeLast = Math.Min(TakeLast, resourceUri.Path.Count);
- //var from = resourceUri.Path.Count - takeLast;
-
- //for (int i = from + 1; i < resourceUri.Path.Count + 1; i++)
- //{
- // var path = resourceUri.Take(i);
-
- // if (path.Display != null)
- // {
- // var display = I18N(context.Culture, path.Display);
- // var href = path.ToString();
-
- // html.Elements.Add
- // (
- // new HtmlElementTextContentLi
- // (
- // //new ControlIcon(Page)
- // //{
- // // Icon = path.Icon
- // //}.ToHtml(),
- // new HtmlElementTextSemanticsA(display)
- // {
- // Href = href,
- // Class = "link"
- // }
- // )
- // {
- // Class = "breadcrumb-item"
- // }
- // );
- // }
- //}
- //}
-
return html;
}
}
diff --git a/src/WebExpress.WebUI/WebControl/ControlButton.cs b/src/WebExpress.WebUI/WebControl/ControlButton.cs
index 8b7a3bc4..66c9441e 100644
--- a/src/WebExpress.WebUI/WebControl/ControlButton.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlButton.cs
@@ -2,12 +2,26 @@
using System.Linq;
using WebExpress.WebCore.Internationalization;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebCore.WebIcon;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a button control.
+ ///
public class ControlButton : Control, IControlButton
{
+ private readonly List _content = [];
+
+ ///
+ /// Returns the content of the control.
+ ///
+ ///
+ /// An enumerable collection of child controls.
+ ///
+ public IEnumerable Content => _content;
+
///
/// Returns or sets the color.
///
@@ -40,11 +54,6 @@ public TypeBlockButton Block
set => SetProperty(value, () => value.ToClass());
}
- ///
- /// Returns or sets the content.
- ///
- public List Content { get; private set; }
-
///
/// Returns or sets the text.
///
@@ -63,7 +72,7 @@ public TypeBlockButton Block
///
/// Returns or sets the icon.
///
- public PropertyIcon Icon { get; set; }
+ public IIcon Icon { get; set; }
///
/// Returns or sets the activation status of the button.
@@ -75,33 +84,72 @@ public TypeActive Active
}
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
- public ControlButton(string id = null)
+ /// The child controls to be added to the button.
+ public ControlButton(string id = null, params IControl[] content)
: base(id)
{
- Init();
+ Size = TypeSizeButton.Default;
+ _content.AddRange(content);
}
///
- /// Initialization
+ /// Adds one or more controls to the content.
///
- private void Init()
+ /// The controls to add to the content.
+ ///
+ /// This method allows adding one or multiple controls to the collection of the control panel.
+ /// It is useful for dynamically constructing the user interface by appending various controls to the panel's content.
+ ///
+ /// Example usage:
+ ///
+ /// var button = new ControlButton();
+ /// var text1 = new ControlText { Text = "A" };
+ /// var text2 = new ControlText { Text = "B" };
+ /// button.Add(text1, text2);
+ ///
+ /// This method accepts any control that implements the interface.
+ ///
+ public void Add(params IControl[] items)
{
- Size = TypeSizeButton.Default;
- Content = new List();
+ _content.AddRange(items);
+ }
+
+ ///
+ /// Adds one or more controls to the content.
+ ///
+ /// The controls to add to the content.
+ ///
+ /// This method allows adding one or multiple controls to the collection of the control panel.
+ /// It is useful for dynamically constructing the user interface by appending various controls to the panel's content.
+ ///
+ /// Example usage:
+ ///
+ /// var button = new ControlButton();
+ /// var text1 = new ControlText { Text = "A" };
+ /// var text2 = new ControlText { Text = "B" };
+ /// button.Add(new List([text1, text2]));
+ ///
+ /// This method accepts any control that implements the interface.
+ ///
+ public void Add(IEnumerable items)
+ {
+ _content.AddRange(items);
}
///
- /// Convert to html.
+ /// Converts the control to an HTML representation.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
var html = new HtmlElementFieldButton()
{
+ Id = Id,
Type = "button",
Value = Value,
Class = Css.Concatenate("btn", GetClasses()),
@@ -110,9 +158,9 @@ public override IHtmlNode Render(RenderContext context)
Disabled = Active == TypeActive.Disabled
};
- if (Icon != null && Icon.HasIcon)
+ if (Icon != null)
{
- html.Elements.Add(new ControlIcon()
+ html.Add(new ControlIcon()
{
Icon = Icon,
Margin = !string.IsNullOrWhiteSpace(Text) ? new PropertySpacingMargin
@@ -122,13 +170,13 @@ public override IHtmlNode Render(RenderContext context)
PropertySpacing.Space.None,
PropertySpacing.Space.None
) : new PropertySpacingMargin(PropertySpacing.Space.None),
- VerticalAlignment = Icon.IsUserIcon ? TypeVerticalAlignment.TextBottom : TypeVerticalAlignment.Default
- }.Render(context));
+ VerticalAlignment = TypeVerticalAlignment.Default
+ }.Render(renderContext, visualTree));
}
if (!string.IsNullOrWhiteSpace(Text))
{
- html.Elements.Add(new HtmlText(InternationalizationManager.I18N(context.Culture, Text)));
+ html.Add(new HtmlText(I18N.Translate(renderContext.Request.Culture, Text)));
}
if (!string.IsNullOrWhiteSpace(OnClick?.ToString()))
@@ -136,29 +184,29 @@ public override IHtmlNode Render(RenderContext context)
html.AddUserAttribute("onclick", OnClick?.ToString());
}
- if (Content.Count > 0)
+ if (_content.Count != 0)
{
- html.Elements.AddRange(Content.Select(x => x.Render(context)));
+ html.Add(_content.Select(x => x.Render(renderContext, visualTree)).ToArray());
}
if (Modal == null || Modal.Type == TypeModal.None)
{
}
- else if (Modal.Type == TypeModal.Formular)
+ else if (Modal.Type == TypeModal.Form)
{
- html.OnClick = $"new webexpress.webui.modalFormularCtrl({{ close: '{InternationalizationManager.I18N(context.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
+ html.OnClick = $"new webexpress.webui.modalFormCtrl({{ close: '{I18N.Translate(renderContext.Request.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
}
else if (Modal.Type == TypeModal.Brwoser)
{
- html.OnClick = $"new webexpress.WebUI.modalPageCtrl({{ close: '{InternationalizationManager.I18N(context.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
+ html.OnClick = $"new webexpress.WebUI.modalPageCtrl({{ close: '{I18N.Translate(renderContext.Request.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
}
else if (Modal.Type == TypeModal.Modal)
{
html.AddUserAttribute("data-bs-toggle", "modal");
html.AddUserAttribute("data-bs-target", "#" + Modal.Modal.Id);
- return new HtmlList(html, Modal.Modal.Render(context));
+ return new HtmlList(html, Modal.Modal.Render(renderContext, visualTree));
}
return html;
diff --git a/src/WebExpress.WebUI/WebControl/ControlButtonLink.cs b/src/WebExpress.WebUI/WebControl/ControlButtonLink.cs
index 79b7317e..b90a2171 100644
--- a/src/WebExpress.WebUI/WebControl/ControlButtonLink.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlButtonLink.cs
@@ -1,64 +1,60 @@
ο»Ώusing System.Linq;
using WebExpress.WebCore.Internationalization;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebCore.WebUri;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a link button control.
+ ///
public class ControlButtonLink : ControlButton
{
///
/// Returns or sets the target uri.
///
- public string Uri { get; set; }
+ public IUri Uri { get; set; }
///
/// Returns or sets the tooltip.
///
- public string Title { get; set; }
+ public string Tooltip { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
- public ControlButtonLink(string id = null)
- : base(id)
+ /// The child controls to be added to the button.
+ public ControlButtonLink(string id = null, params IControl[] content)
+ : base(id, content)
{
- Init();
}
///
- /// Initialization
+ /// Converts the control to an HTML representation.
///
- private void Init()
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
- Size = TypeSizeButton.Default;
- Classes.Add("btn");
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
- {
- var text = InternationalizationManager.I18N(context.Culture, Text);
+ var text = I18N.Translate(Text);
var html = new HtmlElementTextSemanticsA()
{
Id = Id,
- Class = Css.Concatenate(GetClasses(), Size.ToClass()),
+ Class = Css.Concatenate("btn", GetClasses()),
Style = GetStyles(),
Role = Role,
Href = Uri?.ToString(),
- Title = Title,
+ Title = Tooltip,
OnClick = OnClick?.ToString()
};
- if (Icon != null && Icon.HasIcon)
+ if (Icon != null)
{
- html.Elements.Add(new ControlIcon()
+ html.Add(new ControlIcon()
{
Icon = Icon,
Margin = !string.IsNullOrWhiteSpace(Text) ? new PropertySpacingMargin
@@ -68,32 +64,37 @@ public override IHtmlNode Render(RenderContext context)
PropertySpacing.Space.None,
PropertySpacing.Space.None
) : new PropertySpacingMargin(PropertySpacing.Space.None),
- VerticalAlignment = Icon.IsUserIcon ? TypeVerticalAlignment.TextBottom : TypeVerticalAlignment.Default
- }.Render(context));
+ VerticalAlignment = TypeVerticalAlignment.Default
+ }.Render(renderContext, visualTree));
}
if (!string.IsNullOrWhiteSpace(text))
{
- html.Elements.Add(new HtmlText(text));
+ html.Add(new HtmlText(text));
+ }
+
+ if (Content.Any())
+ {
+ html.Add(Content.Select(x => x.Render(renderContext, visualTree)).ToArray());
}
- if (Content.Count > 0)
+ if (!string.IsNullOrWhiteSpace(Tooltip))
{
- html.Elements.AddRange(Content.Select(x => x.Render(context)));
+ html.AddUserAttribute("data-bs-toggle", "tooltip");
}
if (Modal == null || Modal.Type == TypeModal.None)
{
}
- else if (Modal.Type == TypeModal.Formular)
+ else if (Modal.Type == TypeModal.Form)
{
- html.OnClick = $"new webexpress.webui.modalFormularCtrl({{ close: '{InternationalizationManager.I18N(context.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
+ html.OnClick = $"new webexpress.webui.modalFormCtrl({{ close: '{I18N.Translate(renderContext.Request, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
html.Href = "#";
}
else if (Modal.Type == TypeModal.Brwoser)
{
- html.OnClick = $"new webexpress.webui.modalPageCtrl({{ close: '{InternationalizationManager.I18N(context.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
+ html.OnClick = $"new webexpress.webui.modalPageCtrl({{ close: '{I18N.Translate(renderContext.Request, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
html.Href = "#";
}
else if (Modal.Type == TypeModal.Modal)
@@ -101,7 +102,7 @@ public override IHtmlNode Render(RenderContext context)
html.AddUserAttribute("data-bs-toggle", "modal");
html.AddUserAttribute("data-bs-target", "#" + Modal.Modal.Id);
- return new HtmlList(html, Modal.Modal.Render(context));
+ return new HtmlElementTextContentDiv(html, Modal.Modal.Render(renderContext, visualTree));
}
return html;
diff --git a/src/WebExpress.WebUI/WebControl/ControlCanvas.cs b/src/WebExpress.WebUI/WebControl/ControlCanvas.cs
index eb1835f5..78563993 100644
--- a/src/WebExpress.WebUI/WebControl/ControlCanvas.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlCanvas.cs
@@ -1,83 +1,33 @@
-ο»Ώusing System.Collections.Generic;
-using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+ο»Ώusing WebExpress.WebCore.WebHtml;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a canvas control that can contain other controls.
+ ///
+ ///
+ /// The ControlCanvas class provides a container for other controls, allowing for the dynamic construction
+ /// of user interfaces. It supports adding, removing, and rendering child controls.
+ ///
public class ControlCanvas : Control
{
///
- /// Returns or sets the content.
- ///
- public List Content { get; private set; }
-
- ///
- /// Constructor
- ///
- /// The id of the control.
- public ControlCanvas(string id = null)
- : base(id)
- {
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The content of the html element.
- public ControlCanvas(params Control[] content)
- : this()
- {
- Content.AddRange(content);
- }
-
- ///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id.
- /// The content of the html element.
- public ControlCanvas(string id, params Control[] content)
- : this(id)
- {
- Content.AddRange(content);
- }
-
- ///
- /// Constructor
- ///
- /// The id.
- /// The content of the html element.
- public ControlCanvas(string id, IEnumerable content)
- : this(id)
- {
- Content.AddRange(content);
- }
-
- ///
- /// Constructor
- ///
- /// The id.
- /// The content of the html element.
- public ControlCanvas(string id, List content)
+ public ControlCanvas(string id = null)
: base(id)
{
- Content = content;
- }
-
- ///
- /// Initialization
- ///
- private void Init()
- {
- Content = new List();
}
///
- /// Convert to html.
+ /// Converts the control to an HTML representation.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
return new HtmlElementScriptingCanvas()
{
diff --git a/src/WebExpress.WebUI/WebControl/ControlCardCounter.cs b/src/WebExpress.WebUI/WebControl/ControlCardCounter.cs
index 72cd3adc..8371c007 100644
--- a/src/WebExpress.WebUI/WebControl/ControlCardCounter.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlCardCounter.cs
@@ -1,27 +1,28 @@
ο»Ώusing WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebCore.WebIcon;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
///
- /// InformationszΓ€hler
+ /// Represents a counter with an icon, value, progress, and text.
///
- public class ControlCardCounter : ControlPanelCard
+ public class ControlCardCounter : Control
{
///
/// Returns or sets the icon.
///
- public PropertyIcon Icon { get; set; }
+ public IIcon Icon { get; set; }
///
- /// Returns or sets the value. des Counters
+ /// Returns or sets the counter value.
///
- public string Value { get; set; }
+ public int? Value { get; set; }
///
/// Returns or sets the value of the progrss.
///
- public int Progress { get; set; }
+ public uint? Progress { get; set; }
///
/// Returns or sets the text.
@@ -29,46 +30,44 @@ public class ControlCardCounter : ControlPanelCard
public string Text { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlCardCounter(string id = null)
: base(id)
{
TextColor = new PropertyColorText(TypeColorText.Default);
- Init();
}
///
- /// Initialization
+ /// Converts the control to an HTML representation.
///
- private void Init()
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
- Progress = -1;
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
- {
- Content.Clear();
+ var html = new HtmlElementTextSemanticsSpan()
+ {
+ Id = Id,
+ Class = Css.Concatenate("card-counter", GetClasses()),
+ Style = GetStyles(),
+ Role = Role
+ };
- if (Icon != null && Icon.HasIcon)
+ if (Icon != null)
{
- Content.Add(new ControlIcon()
+ html.Add(new ControlIcon()
{
Icon = Icon,
TextColor = TextColor,
HorizontalAlignment = TypeHorizontalAlignment.Right
- });
+ }.Render(renderContext, visualTree));
}
var text = new ControlText(string.IsNullOrWhiteSpace(Id) ? null : Id + "_header")
{
- Text = Value,
+ Text = Value.HasValue ? Value.Value.ToString() : null,
Format = TypeFormatText.H4
};
@@ -79,21 +78,21 @@ public override IHtmlNode Render(RenderContext context)
TextColor = new PropertyColorText(TypeColorText.Muted)
};
- Content.Add(new ControlPanel(text, info) { });
+ html.Add(new ControlPanel(null, text, info) { }.Render(renderContext, visualTree));
- if (Progress > -1)
+ if (Progress.HasValue)
{
- Content.Add(new ControlProgressBar()
+ html.Add(new ControlProgressBar()
{
- Value = Progress,
+ Value = Progress.Value,
Format = TypeFormatProgress.Striped,
BackgroundColor = BackgroundColor,
//Color = Color,
Size = TypeSizeProgress.Small
- });
+ }.Render(renderContext, visualTree));
}
- return base.Render(context);
+ return html;
}
}
}
diff --git a/src/WebExpress.WebUI/WebControl/ControlCarousel.cs b/src/WebExpress.WebUI/WebControl/ControlCarousel.cs
index b5134b40..0cd04fc6 100644
--- a/src/WebExpress.WebUI/WebControl/ControlCarousel.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlCarousel.cs
@@ -1,115 +1,141 @@
-ο»Ώusing System.Collections.Generic;
-using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
-
-namespace WebExpress.WebUI.WebControl
-{
- ///
- /// Creates a slideshow.
- ///
- public class ControlCarousel : Control
- {
- ///
- /// Returns or sets the content.
- ///
- public List Items { get; private set; } = new List();
-
- ///
- /// Constructor
- ///
- /// The id of the control.
- public ControlCarousel(string id = null)
- : base(string.IsNullOrWhiteSpace(id) ? "carousel" : id)
- {
- }
-
- ///
- /// Constructor
- ///
- /// The contents of the slideshow.
- public ControlCarousel(params ControlCarouselItem[] items)
- : this()
- {
- Items.AddRange(items);
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
- {
- // indicators
- var indicators = new HtmlElementTextContentUl() { Class = "carousel-indicators" };
- var index = 0;
-
- foreach (var v in Items)
- {
- var i = new HtmlElementTextContentLi() { Class = index == 0 ? "active" : string.Empty };
- i.AddUserAttribute("data-bs-target", "#" + Id);
- i.AddUserAttribute("data-bs-slide-to", index.ToString());
-
- indicators.Elements.Add(i);
-
- index++;
- }
-
- index = 0;
-
- // items
- var inner = new HtmlElementTextContentDiv() { Class = "carousel-inner" };
- foreach (var v in Items)
- {
- var i = new HtmlElementTextContentDiv(v?.Control.Render(context)) { Class = index == 0 ? "carousel-item active" : "carousel-item" };
-
- if (!string.IsNullOrWhiteSpace(v.Headline) || !string.IsNullOrWhiteSpace(v.Text))
- {
- var caption = new HtmlElementTextContentDiv
- (
- new HtmlElementSectionH3() { Text = v.Headline },
- new HtmlElementTextContentP() { Text = v.Text }
- )
- {
- Class = "carousel-caption"
- };
-
- i.Elements.Add(caption);
- }
-
- inner.Elements.Add(i);
-
- index++;
- }
-
- // navigation
- var navLeft = new HtmlElementTextSemanticsA(new HtmlElementTextSemanticsSpan() { Class = "carousel-control-prev-icon" })
- {
- Class = "carousel-control-prev",
- Href = "#" + Id
- };
- navLeft.AddUserAttribute("data-bs-slide", "prev");
-
- var navRight = new HtmlElementTextSemanticsA(new HtmlElementTextSemanticsSpan() { Class = "carousel-control-next-icon" })
- {
- Class = "carousel-control-next",
- Href = "#" + Id
- };
- navRight.AddUserAttribute("data-bs-slide", "next");
-
- var html = new HtmlElementTextContentDiv
- (
- indicators, inner, navLeft, navRight
- )
- {
- Id = Id,
- Class = Css.Concatenate("carousel slide", GetClasses()),
- Style = GetStyles()
- };
-
- html.AddUserAttribute("data-bs-ride", "carousel");
-
- return html;
- }
- }
-}
+ο»Ώusing System.Collections.Generic;
+using WebExpress.WebCore.WebHtml;
+using WebExpress.WebUI.WebPage;
+
+namespace WebExpress.WebUI.WebControl
+{
+ ///
+ /// Represents a carousel control that can contain multiple carousel items.
+ ///
+ public class ControlCarousel : Control
+ {
+ private readonly List _items = [];
+
+ ///
+ /// Returns the collection of carousel items.
+ ///
+ ///
+ /// An representing the carousel items.
+ ///
+ public IEnumerable Items => _items;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The id of the control.
+ /// The carousel items to be added.
+ public ControlCarousel(string id = null, params ControlCarouselItem[] items)
+ : base(string.IsNullOrWhiteSpace(id) ? "carousel" : id)
+ {
+ _items.AddRange(items);
+ }
+
+ ///
+ /// Adds one or more carousel items to the carousel.
+ ///
+ /// The carousel items to be added.
+ public void Add(params ControlCarouselItem[] items)
+ {
+ _items.AddRange(items);
+ }
+
+ ///
+ /// Adds one or more carousel items to the carousel.
+ ///
+ /// The carousel items to be added.
+ public void Add(IEnumerable items)
+ {
+ _items.AddRange(items);
+ }
+
+ ///
+ /// Clears all carousel items from the carousel.
+ ///
+ public void Clear()
+ {
+ _items.Clear();
+ }
+ ///
+ /// Converts the control to an HTML representation.
+ ///
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
+ {
+ // indicators
+ var indicators = new HtmlElementTextContentUl() { Class = "carousel-indicators" };
+ var index = 0;
+
+ foreach (var v in Items)
+ {
+ var i = new HtmlElementTextContentLi() { Class = index == 0 ? "active" : string.Empty };
+ i.AddUserAttribute("data-bs-target", "#" + Id);
+ i.AddUserAttribute("data-bs-slide-to", index.ToString());
+
+ indicators.Add(i);
+
+ index++;
+ }
+
+ index = 0;
+
+ // items
+ var inner = new HtmlElementTextContentDiv() { Class = "carousel-inner" };
+ foreach (var v in Items)
+ {
+ var i = new HtmlElementTextContentDiv(v?.Control.Render(renderContext, visualTree))
+ {
+ Class = index == 0 ? "carousel-item active" : "carousel-item"
+ };
+
+ if (!string.IsNullOrWhiteSpace(v.Headline) || !string.IsNullOrWhiteSpace(v.Text))
+ {
+ var caption = new HtmlElementTextContentDiv
+ (
+ new HtmlElementSectionH3() { Text = v.Headline },
+ new HtmlElementTextContentP() { Text = v.Text }
+ )
+ {
+ Class = "carousel-caption"
+ };
+
+ i.Add(caption);
+ }
+
+ inner.Add(i);
+
+ index++;
+ }
+
+ // navigation
+ var navLeft = new HtmlElementTextSemanticsA(new HtmlElementTextSemanticsSpan() { Class = "carousel-control-prev-icon" })
+ {
+ Class = "carousel-control-prev",
+ Href = "#" + Id
+ };
+ navLeft.AddUserAttribute("data-bs-slide", "prev");
+
+ var navRight = new HtmlElementTextSemanticsA(new HtmlElementTextSemanticsSpan() { Class = "carousel-control-next-icon" })
+ {
+ Class = "carousel-control-next",
+ Href = "#" + Id
+ };
+ navRight.AddUserAttribute("data-bs-slide", "next");
+
+ var html = new HtmlElementTextContentDiv
+ (
+ indicators, inner, navLeft, navRight
+ )
+ {
+ Id = Id,
+ Class = Css.Concatenate("carousel slide", GetClasses()),
+ Style = GetStyles()
+ };
+
+ html.AddUserAttribute("data-bs-ride", "carousel");
+
+ return html;
+ }
+ }
+}
diff --git a/src/WebExpress.WebUI/WebControl/ControlCarouselItem.cs b/src/WebExpress.WebUI/WebControl/ControlCarouselItem.cs
index 31545ef8..ce670799 100644
--- a/src/WebExpress.WebUI/WebControl/ControlCarouselItem.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlCarouselItem.cs
@@ -1,7 +1,7 @@
ο»Ώnamespace WebExpress.WebUI.WebControl
{
///
- /// Creates a slideshow element.
+ /// Represents an slideshow element in a carousel control.
///
public class ControlCarouselItem
{
@@ -21,11 +21,12 @@ public class ControlCarouselItem
public IControl Control { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class with an optional control.
///
- public ControlCarouselItem()
+ /// The control to be used in the carousel item. If null, no control is set.
+ public ControlCarouselItem(IControl control = null)
{
+ Control = control;
}
-
}
}
diff --git a/src/WebExpress.WebUI/WebControl/ControlChart.cs b/src/WebExpress.WebUI/WebControl/ControlChart.cs
index 2ecc165a..5c3a8ffa 100644
--- a/src/WebExpress.WebUI/WebControl/ControlChart.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlChart.cs
@@ -1,170 +1,173 @@
-ο»Ώusing System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using WebExpress.WebCore.WebComponent;
-using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
-using WebExpress.WebCore.WebUri;
-
-namespace WebExpress.WebUI.WebControl
-{
- public class ControlChart : Control
- {
- ///
- /// Returns or sets the type.
- ///
- public TypeChart Type { get; set; }
-
- ///
- /// Returns or sets the title.
- ///
- public string Title { get; set; }
-
- ///
- /// Returns or sets the title of the x-axis.
- ///
- public string TitleX { get; set; }
-
- ///
- /// Returns or sets the title of the y-axis.
- ///
- public string TitleY { get; set; }
-
- ///
- /// Returns or sets the data.
- ///
- public ICollection Labels { get; set; } = new List();
-
- ///
- /// Returns or sets the width.
- ///
- public new int Width { get; set; }
-
- ///
- /// Returns or sets the height.
- ///
- public new int Height { get; set; }
-
- ///
- /// Returns or sets the minimum.
- ///
- public float Minimum { get; set; } = float.MinValue;
-
- ///
- /// Returns or sets the maximum.
- ///
- public float Maximum { get; set; } = float.MaxValue;
-
- ///
- /// Returns or sets the data.
- ///
- public ICollection Data { get; set; } = new List();
-
- ///
- /// Constructor
- ///
- /// The id of the control.
- public ControlChart(string id = null)
- : base(id)
- {
- }
-
- ///
- /// Initializes the control.
- ///
- /// The context in which the control is rendered.
- public void Initialize(RenderContext context)
- {
- var module = ComponentManager.ModuleManager.GetModule(context.ApplicationContext, typeof(Module));
- if (module != null)
- {
- context.VisualTree.HeaderScriptLinks.Add(UriResource.Combine(module.ContextPath, "/assets/js/Chart.min.js"));
- context.VisualTree.CssLinks.Add(UriResource.Combine(module.ContextPath, "/assets/css/Chart.min.css"));
- }
-
- var builder = new StringBuilder();
- var data = new List();
- builder.Append($"var config_{Id} = {{");
- //if (Type != TypeChart.Polar)
- {
- builder.Append($"type:'{Type.ToType()}',");
- }
- builder.Append("data:{");
- builder.Append($"labels:[{string.Join(",", Labels.Select(x => $"'{x}'"))}],");
- builder.Append("datasets:[{");
- foreach (var v in Data)
- {
- var buf = new StringBuilder();
-
- buf.Append($"label:'{v.Title}',");
- buf.Append($"backgroundColor:{(v.BackgroundColor.Count <= 1 ? v.BackgroundColor.Select(x => $"'{x}'").FirstOrDefault()?.ToString() : $"[ {string.Join(",", v.BackgroundColor.Select(x => $"'{x}'"))} ]")},");
- buf.Append($"borderColor:{(v.BorderColor.Count <= 1 ? v.BorderColor.Select(x => $"'{x}'").FirstOrDefault()?.ToString() : $"[ {string.Join(",", v.BorderColor.Select(x => $"'{x}'"))} ]")},");
- buf.Append($"data:[");
- if (v.Data != null)
- {
- buf.Append(string.Join(",", v.Data.Select(x => x.ToString(CultureInfo.InvariantCulture))));
- }
- buf.Append($"],");
- if (Type == TypeChart.Line)
- {
- buf.Append($"fill:'{v.Fill.ToType()}',");
- buf.Append($"pointStyle:'{v.Point.ToType()}'");
- }
- data.Add(buf);
- }
- builder.Append(string.Join("},{", data));
- builder.Append("}]");
- builder.Append("},");
- builder.Append("options:{");
- builder.Append("responsive:true,");
- builder.Append($"title:{{display:{(string.IsNullOrWhiteSpace(Title) ? "false" : "true")},text:'{Title}'}},");
- builder.Append("tooltips:{mode:'index',intersect:false},");
- builder.Append("hover:{mode:'nearest',intersect:true},");
- if (Type == TypeChart.Line || Type == TypeChart.Bar)
- {
- builder.Append($"scales:{{");
- builder.Append($"xAxes:[{{display: true,scaleLabel:{{display:true,labelString:'{TitleX}'}}}}],");
- builder.Append($"yAxes:[{{display:true,ticks:{{{(Minimum != float.MinValue ? $"min:{Minimum},suggestedMin:{Minimum}," : "")}{(Maximum != float.MaxValue ? $"max:{Maximum},suggestedMax:{Maximum}," : "")}}},scaleLabel:{{display:true,labelString:'{TitleY}'}}}}]");
- builder.Append($"}}");
- }
- builder.Append("}};");
-
- builder.AppendLine($"var chart_{Id} = new Chart(document.getElementById('{Id}').getContext('2d'), config_{Id});");
-
- context.VisualTree.AddScript($"chart_{Id}", builder.ToString());
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
- {
- Initialize(context);
-
- var html = new HtmlElementScriptingCanvas()
- {
- Id = Id,
- Class = Css.Concatenate("", GetClasses()),
- Style = GetStyles(),
- Role = Role
- };
-
- if (Width > 0)
- {
- html.Width = Width;
- html.Style = Css.Concatenate($"width: {Width}px;", html.Style);
- }
-
- if (Height > 0)
- {
- html.Height = Height;
- html.Style = Css.Concatenate($"height: {Height}px;", html.Style);
- }
-
- return new HtmlElementTextContentDiv(html) { Class = "chart-container" };
- }
- }
-}
+ο»Ώusing System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using WebExpress.WebCore.WebEndpoint;
+using WebExpress.WebCore.WebHtml;
+using WebExpress.WebUI.WebPage;
+
+namespace WebExpress.WebUI.WebControl
+{
+ ///
+ /// Represents a chart control that can be used to display various types of charts.
+ ///
+ public class ControlChart : Control
+ {
+ private readonly List _datasets = [];
+
+ ///
+ /// Returns the datasets.
+ ///
+ public IEnumerable Data => _datasets;
+
+ ///
+ /// Returns or sets the labels.
+ ///
+ public ICollection Labels { get; set; } = new List();
+
+ ///
+ /// Returns or sets the type.
+ ///
+ public TypeChart Type { get; set; }
+
+ ///
+ /// Returns or sets the title.
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Returns or sets the title of the x-axis.
+ ///
+ public string TitleX { get; set; }
+
+ ///
+ /// Returns or sets the title of the y-axis.
+ ///
+ public string TitleY { get; set; }
+
+ ///
+ /// Returns or sets the width.
+ ///
+ public new int Width { get; set; }
+
+ ///
+ /// Returns or sets the height.
+ ///
+ public new int Height { get; set; }
+
+ ///
+ /// Returns or sets the minimum.
+ ///
+ public float Minimum { get; set; } = float.MinValue;
+
+ ///
+ /// Returns or sets the maximum.
+ ///
+ public float Maximum { get; set; } = float.MaxValue;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The id of the control.
+ /// The datasets to be used in the chart.
+ public ControlChart(string id = null, params ControlChartDataset[] datasets)
+ : base(id)
+ {
+ _datasets.AddRange(datasets);
+ }
+ ///
+ /// Initializes the control.
+ ///
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ protected void Initialize(IRenderControlContext renderContext, IVisualTreeControl visualTree)
+ {
+ visualTree.AddHeaderScriptLink(RouteEndpoint.Combine(renderContext.PageContext?.ApplicationContext?.ContextPath, "/assets/js/Chart.min.js"));
+ visualTree.AddCssLink(RouteEndpoint.Combine(renderContext.PageContext?.ApplicationContext?.ContextPath, "/assets/css/Chart.min.css"));
+
+ var builder = new StringBuilder();
+ var data = new List();
+ builder.Append($"var config_{Id} = {{");
+ //if (Type != TypeChart.Polar)
+ {
+ builder.Append($"type:'{Type.ToType()}',");
+ }
+ builder.Append("data:{");
+ builder.Append($"labels:[{string.Join(",", Labels.Select(x => $"'{x}'"))}],");
+ builder.Append("datasets:[{");
+ foreach (var v in Data)
+ {
+ var buf = new StringBuilder();
+
+ buf.Append($"label:'{v.Title}',");
+ buf.Append($"backgroundColor:{(v.BackgroundColor.Count <= 1 ? v.BackgroundColor.Select(x => $"'{x}'").FirstOrDefault()?.ToString() : $"[ {string.Join(",", v.BackgroundColor.Select(x => $"'{x}'"))} ]")},");
+ buf.Append($"borderColor:{(v.BorderColor.Count <= 1 ? v.BorderColor.Select(x => $"'{x}'").FirstOrDefault()?.ToString() : $"[ {string.Join(",", v.BorderColor.Select(x => $"'{x}'"))} ]")},");
+ buf.Append($"data:[");
+ if (v.Data != null)
+ {
+ buf.Append(string.Join(",", v.Data.Select(x => x.ToString(CultureInfo.InvariantCulture))));
+ }
+ buf.Append($"],");
+ if (Type == TypeChart.Line)
+ {
+ buf.Append($"fill:'{v.Fill.ToType()}',");
+ buf.Append($"pointStyle:'{v.Point.ToType()}'");
+ }
+ data.Add(buf);
+ }
+ builder.Append(string.Join("},{", data));
+ builder.Append("}]");
+ builder.Append("},");
+ builder.Append("options:{");
+ builder.Append("responsive:true,");
+ builder.Append($"title:{{display:{(string.IsNullOrWhiteSpace(Title) ? "false" : "true")},text:'{Title}'}},");
+ builder.Append("tooltips:{mode:'index',intersect:false},");
+ builder.Append("hover:{mode:'nearest',intersect:true},");
+ if (Type == TypeChart.Line || Type == TypeChart.Bar)
+ {
+ builder.Append($"scales:{{");
+ builder.Append($"xAxes:[{{display: true,scaleLabel:{{display:true,labelString:'{TitleX}'}}}}],");
+ builder.Append($"yAxes:[{{display:true,ticks:{{{(Minimum != float.MinValue ? $"min:{Minimum},suggestedMin:{Minimum}," : "")}{(Maximum != float.MaxValue ? $"max:{Maximum},suggestedMax:{Maximum}," : "")}}},scaleLabel:{{display:true,labelString:'{TitleY}'}}}}]");
+ builder.Append($"}}");
+ }
+ builder.Append("}};");
+
+ builder.AppendLine($"var chart_{Id} = new Chart(document.getElementById('{Id}').getContext('2d'), config_{Id});");
+
+ visualTree.AddScript($"chart_{Id}", builder.ToString());
+ }
+
+ ///
+ /// Converts the control to an HTML representation.
+ ///
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
+ {
+ Initialize(renderContext, visualTree);
+
+ var html = new HtmlElementScriptingCanvas()
+ {
+ Id = Id,
+ Class = Css.Concatenate("", GetClasses()),
+ Style = GetStyles(),
+ Role = Role
+ };
+
+ if (Width > 0)
+ {
+ html.Width = Width;
+ html.Style = Css.Concatenate($"width: {Width}px;", html.Style);
+ }
+
+ if (Height > 0)
+ {
+ html.Height = Height;
+ html.Style = Css.Concatenate($"height: {Height}px;", html.Style);
+ }
+
+ return new HtmlElementTextContentDiv(html) { Class = "chart-container" };
+ }
+ }
+}
diff --git a/src/WebExpress.WebUI/WebControl/ControlChartDataset.cs b/src/WebExpress.WebUI/WebControl/ControlChartDataset.cs
index 04cca96c..148b7e7e 100644
--- a/src/WebExpress.WebUI/WebControl/ControlChartDataset.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlChartDataset.cs
@@ -2,6 +2,9 @@
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a dataset for a chart control.
+ ///
public class ControlChartDataset
{
///
@@ -17,12 +20,12 @@ public class ControlChartDataset
///
/// Returns or sets the background color.
///
- public List BackgroundColor { get; set; } = new List { new PropertyColorChart(TypeColorChart.Primary) };
+ public List BackgroundColor { get; set; } = [new PropertyColorChart(TypeColorChart.Primary)];
///
/// Returns or sets the frame color.
///
- public List BorderColor { get; set; } = new List { new PropertyColorChart(TypeColorChart.Primary) };
+ public List BorderColor { get; set; } = [new PropertyColorChart(TypeColorChart.Primary)];
///
/// Returns or sets how the data series are populated.
@@ -35,7 +38,7 @@ public class ControlChartDataset
public TypePointChart Point { get; set; } = TypePointChart.Circle;
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
public ControlChartDataset()
{
diff --git a/src/WebExpress.WebUI/WebControl/ControlDropdown.cs b/src/WebExpress.WebUI/WebControl/ControlDropdown.cs
index a5b84492..c66a1b48 100644
--- a/src/WebExpress.WebUI/WebControl/ControlDropdown.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlDropdown.cs
@@ -1,16 +1,23 @@
ο»Ώusing System.Collections.Generic;
using System.Linq;
+using WebExpress.WebCore.Internationalization;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebCore.WebIcon;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a dropdown control that can contain multiple items.
+ ///
public class ControlDropdown : Control, IControlNavigationItem
{
+ private readonly List _items = [];
+
///
- /// Returns or sets the content.
+ /// Returns the items in the dropdown.
///
- public List Items { get; private set; } = new List();
+ public IEnumerable Items => _items;
///
/// Returns or sets the background color.
@@ -31,7 +38,7 @@ public TypeSizeButton Size
}
///
- /// Returns or sets the outline property
+ /// Returns or sets the outline property.
///
public bool Outline { get; set; }
@@ -61,7 +68,7 @@ public TypeToggleDropdown Toogle
///
/// Returns or sets the tooltip.
///
- public string Title { get; set; }
+ public string Tooltip { get; set; }
///
/// Returns or sets the value.
@@ -71,7 +78,7 @@ public TypeToggleDropdown Toogle
///
/// Returns or sets the icon.
///
- public PropertyIcon Icon { get; set; }
+ public IIcon Icon { get; set; }
///
/// Returns or sets the activation status of the button.
@@ -107,72 +114,38 @@ public TypeAlignmentDropdownMenu AlignmentMenu
public new int Width { get; set; } = -1;
///
- /// Constructor
+ /// Initializes a new instance of the class with the specified id and items.
///
/// The id of the control.
- public ControlDropdown(string id = null)
- : base(id)
- {
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The id of the control.
- /// The content of the html element.
- public ControlDropdown(string id, params IControlDropdownItem[] content)
- : base(id)
- {
- Items.AddRange(content);
-
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The content of the html element.
- public ControlDropdown(params IControlDropdownItem[] content)
- : base(null)
- {
- Items.AddRange(content);
-
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The id.
- /// The content of the html element.
- public ControlDropdown(string id, IEnumerable content)
+ /// The items to be added to the dropdown.
+ public ControlDropdown(string id = null, params IControlDropdownItem[] items)
: base(id)
{
- Items.AddRange(content);
-
- Init();
- }
-
- ///
- /// Constructor
- ///
- /// The content of the html element.
- public ControlDropdown(IEnumerable content)
- : base(null)
- {
- Items.AddRange(content);
+ _items.AddRange(items);
- Init();
+ Size = TypeSizeButton.Default;
}
///
- /// Adds a new item.
+ /// Adds one or more items to the dropdown.
///
- /// The item.
- public void Add(IControlDropdownItem item)
+ /// The items to add to the dropdown.
+ ///
+ /// This method allows adding one or multiple dropdown items to the collection of
+ /// the dropdown control. It is useful for dynamically constructing the dropdown menu by appending
+ /// various items to it.
+ /// Example usage:
+ ///
+ /// var dropdown = new DropdownControl();
+ /// var item1 = new ControlDropdownItemLink { Text = "Option 1" };
+ /// var item2 = new ControlDropdownItemLink { Text = "Option 2" };
+ /// dropdown.Add(item1, item2);
+ ///
+ /// This method accepts any item that implements the interface.
+ ///
+ public void Add(params IControlDropdownItem[] items)
{
- Items.Add(item);
+ _items.AddRange(items);
}
///
@@ -180,7 +153,7 @@ public void Add(IControlDropdownItem item)
///
public void AddSeperator()
{
- Items.Add(null);
+ _items.Add(null);
}
///
@@ -189,32 +162,28 @@ public void AddSeperator()
/// The headline text.
public void AddHeader(string text)
{
- Items.Add(new ControlDropdownItemHeader() { Text = text });
+ _items.Add(new ControlDropdownItemHeader() { Text = text });
}
///
- /// Initialization
+ /// Converts the control to an HTML representation.
///
- private void Init()
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
- Size = TypeSizeButton.Default;
+ return Render(renderContext, visualTree, Items);
}
///
- /// Adds items.
+ /// Converts the control to an HTML representation.
///
- /// The items which should be added.
- public void Add(params IControlDropdownItem[] item)
- {
- Items.AddRange(item);
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// The items to be included in the dropdown.
+ /// An HTML node representing the rendered control.
+ public virtual IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree, IEnumerable items)
{
var html = new HtmlElementTextContentDiv()
{
@@ -230,14 +199,14 @@ public override IHtmlNode Render(RenderContext context)
Id = string.IsNullOrWhiteSpace(Id) ? "" : Id + "_btn",
Class = Css.Concatenate("btn", Css.Remove(GetClasses(), Margin.ToClass())),
Style = GetStyles(),
- Title = Title
+ Title = I18N.Translate(Tooltip)
};
button.AddUserAttribute("data-bs-toggle", "dropdown");
button.AddUserAttribute("aria-expanded", "false");
- if (Icon != null && Icon.HasIcon)
+ if (Icon != null)
{
- button.Elements.Add(new ControlIcon()
+ button.Add(new ControlIcon()
{
Icon = Icon,
Margin = !string.IsNullOrWhiteSpace(Text) ? new PropertySpacingMargin
@@ -247,16 +216,16 @@ public override IHtmlNode Render(RenderContext context)
PropertySpacing.Space.None,
PropertySpacing.Space.None
) : new PropertySpacingMargin(PropertySpacing.Space.None),
- VerticalAlignment = Icon.IsUserIcon ? TypeVerticalAlignment.TextBottom : TypeVerticalAlignment.Default
- }.Render(context));
+ VerticalAlignment = TypeVerticalAlignment.Default
+ }.Render(renderContext, visualTree));
}
if (!string.IsNullOrWhiteSpace(Text))
{
- button.Elements.Add(new HtmlText(Text));
+ button.Add(new HtmlText(I18N.Translate(renderContext.Request.Culture, Text)));
}
- html.Elements.Add(button);
+ html.Add(button);
}
else
{
@@ -280,22 +249,22 @@ public override IHtmlNode Render(RenderContext context)
button.Width = Width;
}
- html.Elements.Add(button);
+ html.Add(button);
}
- html.Elements.Add
+ html.Add
(
new HtmlElementTextContentUl
(
- Items.Select
+ items.Select
(
x =>
x == null || x is ControlDropdownItemDivider || x is ControlLine ?
new HtmlElementTextContentLi() { Class = "dropdown-divider", Inline = true } :
x is ControlDropdownItemHeader ?
- x.Render(context) :
- new HtmlElementTextContentLi(x.Render(context)) { Class = "dropdown-item " + ((x as ControlDropdownItemLink).Active == TypeActive.Disabled ? "disabled" : "") }
- )
+ x.Render(renderContext, visualTree) :
+ new HtmlElementTextContentLi(x.Render(renderContext, visualTree)) { Class = "dropdown-item " + ((x as ControlDropdownItemLink).Active == TypeActive.Disabled ? "disabled" : "") }
+ ).ToArray()
)
{
Class = Css.Concatenate
@@ -310,9 +279,11 @@ x is ControlDropdownItemHeader ?
.Select(x => x as ControlDropdownItemLink)
.Select(x => x.Modal)
.Where(x => x.Type == TypeModal.Modal)
- .Select(x => x.Modal.Render(context));
+ .Select(x => x.Modal.Render(renderContext, visualTree));
+
+ html.Add(modals);
- return new HtmlList(html, modals);
+ return html;
}
}
}
diff --git a/src/WebExpress.WebUI/WebControl/ControlDropdownItemDivider.cs b/src/WebExpress.WebUI/WebControl/ControlDropdownItemDivider.cs
index d1c17b64..6f163876 100644
--- a/src/WebExpress.WebUI/WebControl/ControlDropdownItemDivider.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlDropdownItemDivider.cs
@@ -1,33 +1,32 @@
ο»Ώusing WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a divider item within a dropdown control.
+ ///
+ ///
+ /// This class is used to create a visual separation between dropdown items.
+ ///
public class ControlDropdownItemDivider : Control, IControlDropdownItem
{
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlDropdownItemDivider(string id = null)
: base(id)
{
- Init();
}
///
- /// Initialization
+ /// Converts the control to an HTML representation.
///
- private void Init()
- {
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
var html = new HtmlElementTextContentDiv()
{
diff --git a/src/WebExpress.WebUI/WebControl/ControlDropdownItemHeader.cs b/src/WebExpress.WebUI/WebControl/ControlDropdownItemHeader.cs
index 5f74ce53..83717b0f 100644
--- a/src/WebExpress.WebUI/WebControl/ControlDropdownItemHeader.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlDropdownItemHeader.cs
@@ -1,8 +1,12 @@
-ο»Ώusing WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+ο»Ώusing WebExpress.WebCore.Internationalization;
+using WebExpress.WebCore.WebHtml;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a header item in a dropdown control.
+ ///
public class ControlDropdownItemHeader : Control, IControlDropdownItem
{
///
@@ -11,43 +15,23 @@ public class ControlDropdownItemHeader : Control, IControlDropdownItem
public string Text { get; set; }
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
public ControlDropdownItemHeader(string id = null)
: base(id)
{
- Init();
}
///
- /// Constructor
+ /// Converts the control to an HTML representation.
///
- /// The id of the control.
- /// The text.
- public ControlDropdownItemHeader(string id, string text)
- : base(id)
- {
- Text = text;
-
- Init();
- }
-
- ///
- /// Initialization
- ///
- private void Init()
- {
- }
-
- ///
- /// Convert to html.
- ///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
- return new HtmlElementTextContentLi(new HtmlText(Text))
+ return new HtmlElementTextContentLi(new HtmlText(I18N.Translate(renderContext.Request.Culture, Text)))
{
Id = Id,
Class = Css.Concatenate("dropdown-header", GetClasses()),
diff --git a/src/WebExpress.WebUI/WebControl/ControlDropdownItemLink.cs b/src/WebExpress.WebUI/WebControl/ControlDropdownItemLink.cs
index 0adb46e7..226ed1e4 100644
--- a/src/WebExpress.WebUI/WebControl/ControlDropdownItemLink.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlDropdownItemLink.cs
@@ -1,31 +1,36 @@
ο»Ώusing System.Linq;
using WebExpress.WebCore.Internationalization;
using WebExpress.WebCore.WebHtml;
-using WebExpress.WebCore.WebPage;
+using WebExpress.WebUI.WebPage;
namespace WebExpress.WebUI.WebControl
{
+ ///
+ /// Represents a dropdown item link control.
+ ///
public class ControlDropdownItemLink : ControlLink, IControlDropdownItem
{
///
- /// Constructor
+ /// Initializes a new instance of the class.
///
/// The id of the control.
- public ControlDropdownItemLink(string id = null)
- : base(id)
+ /// The content of the html element.
+ public ControlDropdownItemLink(string id = null, params IControl[] content)
+ : base(id, content)
{
}
///
- /// Convert to html.
+ /// Converts the control to an HTML representation.
///
- /// The context in which the control is rendered.
- /// The control as html.
- public override IHtmlNode Render(RenderContext context)
+ /// The context in which the control is rendered.
+ /// The visual tree representing the control's structure.
+ /// An HTML node representing the rendered control.
+ public override IHtmlNode Render(IRenderControlContext renderContext, IVisualTreeControl visualTree)
{
- var param = GetParams(context?.Page);
+ var param = ""; // GetParams(context?.Page);
- var html = new HtmlElementTextSemanticsA(from x in Content select x.Render(context))
+ var html = new HtmlElementTextSemanticsA([.. Controls.Select(x => x.Render(renderContext, visualTree))])
{
Id = Id,
Class = Css.Concatenate("link", GetClasses()),
@@ -33,13 +38,13 @@ public override IHtmlNode Render(RenderContext context)
Role = Role,
Href = Uri?.ToString() + (param.Length > 0 ? "?" + param : string.Empty),
Target = Target,
- Title = InternationalizationManager.I18N(context.Culture, Title),
+ Title = string.IsNullOrEmpty(Title) ? I18N.Translate(renderContext.Request, Tooltip) : I18N.Translate(renderContext.Request, Title),
OnClick = OnClick?.ToString()
};
- if (Icon != null && Icon.HasIcon)
+ if (Icon != null)
{
- html.Elements.Add(new ControlIcon()
+ html.Add(new ControlIcon()
{
Icon = Icon,
Margin = !string.IsNullOrWhiteSpace(Text) ? new PropertySpacingMargin
@@ -49,26 +54,26 @@ public override IHtmlNode Render(RenderContext context)
PropertySpacing.Space.None,
PropertySpacing.Space.None
) : new PropertySpacingMargin(PropertySpacing.Space.None)
- }.Render(context));
+ }.Render(renderContext, visualTree));
}
if (!string.IsNullOrWhiteSpace(Text))
{
- html.Elements.Add(new HtmlText(InternationalizationManager.I18N(context.Culture, Text)));
+ html.Add(new HtmlText(I18N.Translate(renderContext.Request, Text)));
}
if (Modal == null || Modal.Type == TypeModal.None)
{
}
- else if (Modal.Type == TypeModal.Formular)
+ else if (Modal.Type == TypeModal.Form)
{
- html.OnClick = $"new webexpress.webui.modalFormularCtrl({{ close: '{InternationalizationManager.I18N(context.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
+ html.OnClick = $"new webexpress.webui.modalFormCtrl({{ close: '{I18N.Translate(renderContext.Request, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
html.Href = "#";
}
else if (Modal.Type == TypeModal.Brwoser)
{
- html.OnClick = $"new webexpress.webui.modalPageCtrl({{ close: '{InternationalizationManager.I18N(context.Culture, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
+ html.OnClick = $"new webexpress.webui.modalPageCtrl({{ close: '{I18N.Translate(renderContext.Request, "webexpress.webui:form.cancel.label")}', uri: '{Modal.Uri?.ToString() ?? html.Href}', size: '{Modal.Size.ToString().ToLower()}', redirect: '{Modal.RedirectUri}'}});";
html.Href = "#";
}
else if (Modal.Type == TypeModal.Modal)
diff --git a/src/WebExpress.WebUI/WebControl/ControlForm.cs b/src/WebExpress.WebUI/WebControl/ControlForm.cs
index 79cde17e..8d49cadd 100644
--- a/src/WebExpress.WebUI/WebControl/ControlForm.cs
+++ b/src/WebExpress.WebUI/WebControl/ControlForm.cs
@@ -1,20 +1,31 @@
ο»Ώusing System;
using System.Collections.Generic;
using System.Linq;
+using WebExpress.WebCore;
using WebExpress.WebCore.Internationalization;
+using WebExpress.WebCore.WebFragment;
using WebExpress.WebCore.WebHtml;
using WebExpress.WebCore.WebMessage;
using WebExpress.WebCore.WebPage;
-using static WebExpress.WebCore.Internationalization.InternationalizationManager;
+using WebExpress.WebCore.WebScope;
+using WebExpress.WebUI.WebFragment;
+using WebExpress.WebUI.WebPage;
+using WebExpress.WebUI.WebSection;
namespace WebExpress.WebUI.WebControl
{
- public class ControlForm : Control, IControlForm
+ ///
+ /// Represents a form with various input fields and controls.
+ ///
+ public class ControlForm : Control, IControlForm, IScope
{
- ///
- /// Returns or sets the layout.
- ///
- public virtual TypeLayoutForm Layout { get; set; } = TypeLayoutForm.Vertical;
+ private readonly List _items = [];
+ private readonly List _preferencesButtons = [];
+ private readonly List _primaryButtons = [];
+ private readonly List _secondaryButtons = [];
+ private readonly List _preferencesControls = [];
+ private readonly List _primaryControls = [];
+ private readonly List _secondaryControls = [];
///
/// Event to validate the input values.
@@ -29,22 +40,27 @@ public class ControlForm : Control, IControlForm
///
/// Event is raised when the form has been initialized.
///
- public event EventHandler InitializeFormular;
+ public event EventHandler InitializeForm;
///
/// Event is raised when the form's data needs to be determined.
///
- public event EventHandler FillFormular;
+ public event EventHandler FillForm;
///
/// Event is raised when the form is about to be processed.
///
- public event EventHandler ProcessFormular;
+ public event EventHandler ProcessForm;
///
/// Event is raised when the form is to be processed and the next data is to be loaded.
///
- public event EventHandler ProcessAndNextFormular;
+ public event EventHandler ProcessAndNextForm;
+
+ ///
+ /// Returns the form items.
+ ///
+ public IEnumerable Items => _items;
///
/// Returns or sets the name of the form.
@@ -61,66 +77,119 @@ public class ControlForm : Control, IControlForm
///
public string RedirectUri { get; set; }
+ ///
+ /// Returns or sets the form layout.
+ ///
+ public virtual TypeLayoutForm FormLayout { get; set; } = TypeLayoutForm.Default;
+
+ ///
+ /// Returns or sets the item layout.
+ ///
+ public virtual TypeLayoutFormItem ItemLayout { get; set; } = TypeLayoutFormItem.Vertical;
+
///
/// Returns or sets the hidden field that contains the id.
///
- public ControlFormItemInputHidden FormularId { get; } = new ControlFormItemInputHidden(Guid.NewGuid().ToString())
+ public ControlFormItemInputHidden FormId { get; } = new ControlFormItemInputHidden(Guid.NewGuid().ToString())
{
- Name = "formular-id"
+ Name = "form-id"
};
///
/// Returns or sets the hidden field that contains the submit method.
///
- public ControlFormItemInputHidden SubmitType { get; } = new ControlFormItemInputHidden(Guid.NewGuid().ToString())
+ public ControlFormItemInputHidden SubmitHiddenField { get; } = new ControlFormItemInputHidden(Guid.NewGuid().ToString())
{
- Name = "formular-submit-type",
+ Name = "form-submit-type",
Value = "update"
};
///
- /// Returns or sets the submit button.
+ /// Returns or sets the request method.
///
- public ControlFormItemButton SubmitButton { get; } = new ControlFormItemButton()
- {
- Icon = new PropertyIcon(TypeIcon.Save),
- Color = new PropertyColorButton(TypeColorButton.Success),
- Type = TypeButton.Submit,
- Margin = new PropertySpacingMargin(PropertySpacing.Space.None, PropertySpacing.Space.Two, PropertySpacing.Space.None, PropertySpacing.Space.None),
- };
+ public RequestMethod Method { get; set; } = RequestMethod.POST;
+
+ ///
+ /// Returns or sets the header preferences section.
+ ///
+ protected List HeaderPreferences { get; } = [];
+
+ ///
+ /// Returns or sets the header primary section.
+ ///
+ protected List HeaderPrimary { get; } = [];
+
+ ///
+ /// Returns or sets the header secondary section.
+ ///
+ protected List HeaderSecondary { get; } = [];
+
+ ///
+ /// Returns or sets the button panel preferences section.
+ ///
+ protected List ButtonPanelPreferences { get; } = [];
+
+ ///
+ /// Returns or sets the button panel primary section.
+ ///
+ protected List ButtonPanelPrimary { get; } = [];
+
+ ///