Spring Boot Admin Server supports cluster replication via Hazelcast. It is automatically enabled when a HazelcastConfig- or HazelcastInstance-Bean is present. You can also configure the Hazelcast instance to be persistent, to keep the status over restarts. Also have a look at the Spring Boot support for Hazelcast.
@Bean
+public Config hazelcastConfig() {
+ // This map is used to store the events.
+ // It should be configured to reliably hold all the data,
+ // Spring Boot Admin will compact the events, if there are too many
+ MapConfig eventStoreMap = new MapConfig(DEFAULT_NAME_EVENT_STORE_MAP).setInMemoryFormat(InMemoryFormat.OBJECT)
+ .setBackupCount(1)
+ .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100));
+
+ // This map is used to deduplicate the notifications.
+ // If data in this map gets lost it should not be a big issue as it will atmost
+ // lead to
+ // the same notification to be sent by multiple instances
+ MapConfig sentNotificationsMap = new MapConfig(DEFAULT_NAME_SENT_NOTIFICATIONS_MAP)
+ .setInMemoryFormat(InMemoryFormat.OBJECT)
+ .setBackupCount(1)
+ .setEvictionConfig(
+ new EvictionConfig().setEvictionPolicy(EvictionPolicy.LRU).setMaxSizePolicy(MaxSizePolicy.PER_NODE))
+ .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100));
+
+ Config config = new Config();
+ config.addMapConfig(eventStoreMap);
+ config.addMapConfig(sentNotificationsMap);
+ config.setProperty("hazelcast.jmx", "true");
+
+ // WARNING: This setups a local cluster, you change it to fit your needs.
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ TcpIpConfig tcpIpConfig = config.getNetworkConfig().getJoin().getTcpIpConfig();
+ tcpIpConfig.setEnabled(true);
+ tcpIpConfig.setMembers(singletonList("127.0.0.1"));
+ return config;
+}
+
+
+
+
+
+
+ Table 1. Hazelcast configuration options
+
+
+
+
+
+
+
+
+
Property name
+
Description
+
Default value
+
+
+
+
+
spring.boot.admin.hazelcast.enabled
+
Enables the Hazelcast support
+
true
+
+
+
spring.boot.admin.hazelcast.event-store
+
Name of the Hazelcast-map to store the events
+
"spring-boot-admin-event-store"
+
+
+
spring.boot.admin.hazelcast.sent-notifications
+
Name of the Hazelcast-map used to deduplicate the notifications.
The Spring Boot Admin Server can use Spring Clouds DiscoveryClient to discover applications. The advantage is that the clients don’t have to include the spring-boot-admin-starter-client. You just have to add a DiscoveryClient implementation to your admin server - everything else is done by AutoConfiguration.
+
+
+
Static Configuration using SimpleDiscoveryClient
+
+
Spring Cloud provides a SimpleDiscoveryClient. It allows you to specify client applications via static configuration:
Spring Boot Admin supports all other implementations of Spring Cloud’s DiscoveryClient (Eureka, Zookeeper, Consul, Kubernetes, …). You need to add it to the Spring Boot Admin Server and configure it properly. An example setup using Eureka is shown above.
+
+
+
+
Converting ServiceInstances
+
+
The information from the service registry are converted by the ServiceInstanceConverter. Spring Boot Admin ships with a default and Eureka converter implementation. The correct one is selected by AutoConfiguration.
+
+
+
+
+
+
+
You can modify how the information from the registry is used to register the application by using SBA Server configuration options and instance metadata. The values from the metadata takes precedence over the server config. If the plenty of options don’t fit your needs you can provide your own ServiceInstanceConverter.
+
+
+
+
+
+
+
+
+
+
When using Eureka, the healthCheckUrl known to Eureka is used for health-checking, which can be set on your client using eureka.instance.healthCheckUrl.
+
+
+
+
+
+
+ Table 1. Instance metadata options
+
+
+
+
+
+
+
+
+
Key
+
Value
+
Default value
+
+
+
+
+
user.name user.password
+
Credentials being used to access the endpoints.
+
+
+
+
management.scheme
+
The scheme is substituted in the service URL and will be used for accessing the actuator endpoints.
+
+
+
+
management.address
+
The address is substituted in the service URL and will be used for accessing the actuator endpoints.
+
+
+
+
management.port
+
The port is substituted in the service URL and will be used for accessing the actuator endpoints.
+
+
+
+
management.context-path
+
The path is appended to the service URL and will be used for accessing the actuator endpoints.
Instances of services will be ignored if they contain at least one metadata item that matches this list. (e.g. "discoverable=false")
+
+
+
+
spring.boot.admin.discovery.instances-metadata
+
Instances of services will be included if they contain at least one metadata item that matches this list. (e.g. "discoverable=true")
+
+
+
+
+
+
+
CloudFoundry
+
+
If you are deploying your applications to CloudFoundry then vcap.application.application_id and vcap.application.instance_indexmust be added to the metadata for proper registration of applications with Spring Boot Admin Server. Here is a sample configuration for Eureka:
For Spring Boot applications the easiest way to show the version, is to use the build-info goal from the spring-boot-maven-plugin, which generates the META-INF/build-info.properties. See also the Spring Boot Reference Guide.
+
+
+
For non-Spring Boot applications you can either add a version or build.version to the registration metadata and the version will show up in the application list.
To generate the build-info in a gradle project, add the following snippet to your build.gradle:
+
+
+
+ build.gradle
+
+
+
springBoot {
+ buildInfo()
+}
+
+
+
+
+
+
JMX-Bean Management
+
+
+
ATTENTION: Spring Boot 3 does currently not support Jolokia, so this will not work with Spring Boot 3 based applications. You can still monitor Spring Boot 2 applications with Jolokia endpoint using a Spring Boot Admin 3 server.
+
+
+
To interact with JMX-beans in the admin UI you have to include Jolokia in your application. As Jolokia is servlet based there is no support for reactive applications. In case you are using the spring-boot-admin-starter-client it will be pulled in for you, if not add Jolokia to your dependencies. With Spring Boot 2.2.0 you might want to set spring.jmx.enabled=true if you want to expose Spring beans via JMX.
By default the logfile is not accessible via actuator endpoints and therefore not visible in Spring Boot Admin. In order to enable the logfile actuator endpoint you need to configure Spring Boot to write a logfile, either by setting logging.file.path or logging.file.name.
+
+
+
Spring Boot Admin will detect everything that looks like an URL and render it as hyperlink.
+
+
+
ANSI color-escapes are also supported. You need to set a custom file log pattern as Spring Boot’s default one doesn’t use colors.
+
+
+
To enforce the use of ANSI-colored output, set spring.output.ansi.enabled=ALWAYS. Otherwise Spring tries to detect if ANSI-colored output is available and might disable it.
Destination the logfile is written to. Enables the logfile actuator endpoint.
+
+
+
2
+
File log pattern using ANSI colors.
+
+
+
+
+
+
+
+
Show Tags per Instance
+
+
+
Tags are a way to add visual markers per instance, they will appear in the application list as well as in the instance view. By default no tags are added to instances, and it’s up to the client to specify the desired tags by adding the information to the metadata or info endpoint.
+
+
+
+ application.properties
+
+
+
#using the metadata
+spring.boot.admin.client.instance.metadata.tags.environment=test
+
+#using the info endpoint
+info.tags.environment=test
+
+
+
+
+
+
Spring Boot Admin Client
+
+
+
The Spring Boot Admin Client registers the application at the admin server. This is done by periodically doing a HTTP post request to the SBA Server providing information about the application.
+
+
+
+
+
+
+
There are plenty of properties to influence the way how the SBA Client registers your application. In case that doesn’t fit your needs, you can provide your own ApplicationFactory implementation.
+
+
+
+
+
+
+ Table 1. Spring Boot Admin Client configuration options
+
+
+
+
+
+
+
+
+
Property name
+
Description
+
Default value
+
+
+
+
+
spring.boot.admin.client.enabled
+
Enables the Spring Boot Admin Client.
+
true
+
+
+
spring.boot.admin.client.url
+
Comma separated ordered list of URLs of the Spring Boot Admin server to register at. This triggers the AutoConfiguration. Mandatory.
+
+
+
+
spring.boot.admin.client.api-path
+
Http-path of registration endpoint at your admin server.
Username and password in case the SBA Server api is protected with HTTP Basic authentication.
+
+
+
+
spring.boot.admin.client.period
+
Interval for repeating the registration (in ms).
+
10,000
+
+
+
spring.boot.admin.client.connect-timeout
+
Connect timeout for the registration (in ms).
+
5,000
+
+
+
spring.boot.admin.client.read-timeout
+
Read timeout for the registration (in ms).
+
5,000
+
+
+
spring.boot.admin.client.auto-registration
+
If set to true the periodic task to register the application is automatically scheduled after the application is ready.
+
true
+
+
+
spring.boot.admin.client.auto-deregistration
+
Switch to enable auto-deregistration at Spring Boot Admin server when context is closed. If the value is unset the feature is active if a running CloudPlatform was detected.
+
null
+
+
+
spring.boot.admin.client.register-once
+
If set to true the client will only register against one admin server (in order defined by spring.boot.admin.instance.url); if that admin server goes down, will automatically register against the next admin server. If false, will register against all admin servers.
+
true
+
+
+
spring.boot.admin.client.instance.health-url
+
Health-url to register with. Can be overridden in case the reachable URL is different (e.g. Docker). Must be unique in registry.
+
Guessed based on management-base-url and endpoints.health.id.
Base url for computing the service-url to register with. The path is inferred at runtime, and appended to the base url. In Cloudfoundry environments you can switching to https like this: spring.boot.admin.client.instance.service-base-url=https://${vcap.application.uris[0]}
+
Guessed based on hostname, server.port.
+
+
+
spring.boot.admin.client.instance.service-url
+
Service-url to register with. Can be overridden in case the reachable url is different (e.g. Docker).
+
Guessed based on service-base-url and server.context-path.
+
+
+
spring.boot.admin.client.instance.service-path
+
Service-path to register with. Can be overridden in case the reachable path is different (e.g. context-path set programmatically).
+
/
+
+
+
spring.boot.admin.client.instance.name
+
Name to register with.
+
${spring.application.name} if set, "spring-boot-application" otherwise.
Select which information should be considered when sending the host of a service: * IP: Uses the IP returned by InetAddress.getHostAddress() * HOST_NAME: Uses the host name of a single machine returned by InetAddress.getHostName() * CANONICAL_HOST_NAME: Uses the FQDN returned by InetAddress.geCanonicalHostName() If server.address or management.server.address is set in the service, the value will overrule this property.
+
CANONICAL_HOST_NAME
+
+
+
spring.boot.admin.client.instance.metadata.*
+
Metadata key-value-pairs to be associated with this instance.
+
+
+
+
spring.boot.admin.client.instance.metadata.tags.*
+
Tags as key-value-pairs to be associated with this instance.
+
+
+
+
+
+
+ Table 2. Instance metadata options
+
+
+
+
+
+
+
+
+
Key
+
Value
+
Default value
+
+
+
+
+
user.name user.password
+
Credentials being used to access the endpoints.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/3.3.3/css/common.css b/3.3.3/css/common.css
new file mode 100644
index 00000000000..b1a71ed0e8a
--- /dev/null
+++ b/3.3.3/css/common.css
@@ -0,0 +1,10 @@
+/*!
+ * Licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/*!
+ * General-use styles, meant to be applied to all the pages on the site.
+ */
+
+body {
+ min-height: 100vh;
+}
diff --git a/3.3.3/css/custom.css b/3.3.3/css/custom.css
new file mode 100644
index 00000000000..6fc41682b6d
--- /dev/null
+++ b/3.3.3/css/custom.css
@@ -0,0 +1,114 @@
+.shadow {
+ box-shadow: none !important;
+}
+
+body::before {
+ background-image: url("../images/wave.svg");
+ background-position: bottom;
+ background-repeat: no-repeat;
+ background-size: 100%;
+ content: "";
+ display: block;
+ height: 200px;
+ left: 0;
+ position: fixed;
+ width: 100%;
+ z-index: -1;
+}
+
+.navbar {
+ position: fixed;
+ width: 100%;
+ top: 0;
+ z-index: 1;
+ border-bottom: 1px solid #333;
+}
+
+.navbar.bg-light {
+ background-color: rgba(255, 255, 255, 0.5) !important;
+ backdrop-filter: blur(10px);
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
+}
+
+#main-section {
+ display: flex;
+ flex-direction: column;
+ margin-top: 80px;
+}
+
+#navbar-footer dt {
+ display: none;
+}
+
+#navbar-footer dl {
+ display: flex;
+ flex-direction: row;
+ gap: 2rem;
+ justify-content: center;
+}
+
+
+#footer-info {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ line-height: 2em;
+ font-size: .8em;
+}
+
+.hljs {
+ background-color: rgba(240, 240, 240, .75);
+ backdrop-filter: blur(10px);
+}
+
+#footer-info a, #navbar-footer a {
+ color: #000;
+}
+
+#footer-info a {
+ padding: 0 .5rem;
+}
+
+/* Content */
+.listingblock {
+ position: relative;
+}
+
+.listingblock .title {
+ position: absolute;
+ right: 0;
+ top: 0;
+ border-radius: 0.25rem;
+ border: 1px solid #ddd;
+ background-color: #fff;
+ padding: 0 0.5rem;
+}
+
+header.border-bottom {
+ border: none !important;
+}
+
+.colist tr td:first-child {
+ text-align: right;
+}
+
+.hero {
+ flex: 1 0 0;
+
+ display: flex;
+ align-items: center;
+ margin: 0 25%;
+}
+
+.hero .title {
+ font-size: 4rem;
+ line-height: 4rem;
+ margin-bottom: 4rem;
+ text-align: center;
+}
+
+.hero .paragraph p {
+ font-size: 1.5rem;
+ font-weight: 400;
+ text-align: center;
+}
diff --git a/3.3.3/css/link.css b/3.3.3/css/link.css
new file mode 100644
index 00000000000..26f2d0f38db
--- /dev/null
+++ b/3.3.3/css/link.css
@@ -0,0 +1,28 @@
+/*!
+ * Licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/*!
+ * Link styles.
+ */
+
+/**
+* Link with a note.
+*
+* It will be shown like: "Link (note)".
+*
+* The note should be marked with note right next to the link name.
+*
+* So a noted link would be: Link note.
+*/
+
+.link-noted .note {
+ font-size: 65%;
+}
+
+.link-noted .note:before {
+ content: "(";
+}
+
+.link-noted .note:after {
+ content: ")";
+}
\ No newline at end of file
diff --git a/3.3.3/css/site.css b/3.3.3/css/site.css
new file mode 100644
index 00000000000..055e7e286ad
--- /dev/null
+++ b/3.3.3/css/site.css
@@ -0,0 +1 @@
+/* You can override this file with your own styles */
\ No newline at end of file
diff --git a/3.3.3/css/style.min.css b/3.3.3/css/style.min.css
new file mode 100644
index 00000000000..bb7f24e8539
--- /dev/null
+++ b/3.3.3/css/style.min.css
@@ -0,0 +1,9 @@
+/*!
+ * Licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
+ *//*!
+ * General-use styles, meant to be applied to all the pages on the site.
+ */body{min-height:100vh}/*!
+ * Licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
+ *//*!
+ * Link styles.
+ */.link-noted .note{font-size:65%}.link-noted .note:before{content:"("}.link-noted .note:after{content:")"}
\ No newline at end of file
diff --git a/3.3.3/customize_http-headers.html b/3.3.3/customize_http-headers.html
new file mode 100644
index 00000000000..e8166332ce2
--- /dev/null
+++ b/3.3.3/customize_http-headers.html
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+ Spring Boot Admin – [Untitled]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Injecting Custom HTTP Headers
+
+
+
In case you need to inject custom HTTP headers into the requests made to the monitored application’s actuator endpoints you can easily add a HttpHeadersProvider:
You can intercept and modify requests and responses made to the monitored application’s actuator endpoints by implementing the InstanceExchangeFilterFunction interface. This can be useful for auditing or adding some extra security checks.
You can add your own Notifiers by adding Spring Beans which implement the Notifier interface, at best by extending AbstractEventNotifier or AbstractStatusChangeNotifier.
It is possible to add custom views to the ui. The views must be implemented as Vue.js components.
+
+
+
The JavaScript-Bundle and CSS-Stylesheet must be placed on the classpath at /META-INF/spring-boot-admin-server-ui/extensions/{name}/ so the server can pick them up. The spring-boot-admin-sample-custom-ui module contains a sample which has the necessary maven setup to build such a module.
The imported custom component, which will be rendered on the route.
+
+
+
4
+
An optional group name that allows to bind views to a logical group (defaults to "none")
+
+
+
5
+
The handle for the custom view to be shown in the top navigation bar.
+
+
+
6
+
Order for the view.
+
+
+
7
+
Using i18n.mergeLocaleMessage allows to add custom translations.
+
+
+
+
+
+
Views in the top navigation bar are sorted by ascending order.
+
+
+
If new top level routes are added to the frontend, they also must be known to the backend. Add a /META-INF/spring-boot-admin-server-ui/extensions/{name}/routes.txt with all your new toplevel routes (one route per line).
+
+
+
Groups are used in instance sidebar to aggregate multiple views into a collapsible menu entry showing the group’s name. When a group contains just a single element, the label of the view is shown instead of the group’s name.
+
+
+
Override/Set custom group icons
+
+
In order to override or set icons for (custom) groups you can use the SBA.viewRegistry.setGroupIcon function as follows:
The parent must be 'instances' in order to render the new custom view for a single instance.
+
+
+
2
+
You can group views by assigning them to a group.
+
+
+
3
+
If you add a isEnabled callback you can figure out dynamically if the view should be show for the particular instance.
+
+
+
+
+
+
+
+
+
+
You can override default views by putting the same group and name as the one you want to override.
+
+
+
+
+
+
+
Customizing Header Logo and Title
+
+
You can set custom information in the header (i.e. displaying staging information or company name) by using following configuration properties:
+
+
+
+
spring.boot.admin.ui.brand: This HTML snippet is rendered in navigation header and defaults to <img src="assets/img/icon-spring-boot-admin.svg"><span>Spring Boot Admin</span>. By default it shows the SBA logo followed by it’s name. If you want to show a custom logo you can set: spring.boot.admin.ui.brand=<img src="custom/custom-icon.png">. Either you just add the image to your jar-file in /META-INF/spring-boot-admin-server-ui/ (SBA registers a ResourceHandler for this location by default), or you must ensure yourself that the image gets served correctly (e.g. by registering your own ResourceHandler)
+
spring.boot.admin.ui.title: Use this option to customize the browsers window title.
+
+
+
+
+
+
+
Customizing Colors
+
+
+
You can provide a custom color theme to the application by overwriting the following properties:
Define a color palette that affects the colors in sidebar view (e.g shade 600 of palette is used as text color and shade 50 as background color.)
+
+
+
+
+
+
+
Customizing Login Logo
+
+
+
You can set a custom image to be displayed on the login page.
+
+
+
+
Put the image in a resource location which is served via http (e.g. /META-INF/spring-boot-admin-server-ui/assets/img/).
+
Configure the icons to use using the following property:
+
+
+
spring.boot.admin.ui.login-icon: Used as icon on login page. (e.g assets/img/custom-login-icon.svg)
+
+
+
+
+
+
+
+
Customizing Favicon
+
+
+
It is possible to use a custom favicon, which is also used for desktop notifications. Spring Boot Admin uses a different icon when one or more application is down.
+
+
+
+
Put the favicon (.png with at least 192x192 pixels) in a resource location which is served via http (e.g. /META-INF/spring-boot-admin-server-ui/assets/img/).
+
Configure the icons to use using the following properties:
+
+
+
spring.boot.admin.ui.favicon: Used as default icon. (e.g assets/img/custom-favicon.png
+
spring.boot.admin.ui.favicon-danger: Used when one or more service is down. (e.g assets/img/custom-favicon-danger.png)
+
+
+
+
+
+
+
+
Customizing Available Languages
+
+
+
To filter languages to a subset of all supported languages:
+
+
+
+
spring.boot.admin.ui.available-languages: Used as a filter of existing languages. (e.g en,de out of existing de,en,fr,ko,pt-BR,ru,zh)
+ Can I include spring-boot-admin into my business application?
+
+
+
tl;dr You can, but you shouldn’t. You can set spring.boot.admin.context-path to alter the path where the UI and REST-API is served, but depending on the complexity of your application you might get in trouble. On the other hand in my opinion it makes no sense for an application to monitor itself. In case your application goes down your monitoring tool also does.
+
+
+ Can I change or reload Spring Boot properties at runtime?
+
Spring Boot Admin is a monitoring tool that aims to visualize information provided by Spring Boot Actuators in a nice and accessible way. It consists of two major parts:
+
+
+
+
A server that provides a user interface to display and interact with Spring Boot Actuators.
+
A client that is used to register at the server and allow to access actuator endpoints.
+
+
+
+
+
+
Quick Start
+
+
+
Since Spring Boot Admin relies on Spring Boot, you have to set up a Spring Boot application first. We recommend doing this by using http://start.spring.io. Spring Boot Admin Server is capable of running as servlet or webflux application, you need to decide on this and add the according Spring Boot Starter. In this example we’re using the servlet web starter.
+
+
+
+
Add Spring Boot Admin Server starter to your dependencies:
Each application that wants to register has to include the Spring Boot Admin Client. In order to secure the endpoints, also add the spring-boot-starter-security.
+
+
+
+
Add spring-boot-admin-starter-client to your dependencies:
The URL of the Spring Boot Admin Server to register at.
+
+
+
2
+
As with Spring Boot 2 most of the endpoints aren’t exposed via http by default, we expose all of them. For production you should carefully choose which endpoints to expose.
+
+
+
3
+
Since Spring Boot 2.6, env info contributor is disabled by default. Hence, we have to enable it.
For the sake of brevity we’re disabling the security for now. Have a look at the security section on how to deal with secured endpoints.
+
+
+
+
+
+
+
+
+
Spring Cloud Discovery
+
+
If you already use Spring Cloud Discovery for your applications you don’t need the SBA Client. Just add a DiscoveryClient to Spring Boot Admin Server, the rest is done by our AutoConfiguration.
+
+
+
The following steps uses Eureka, but other Spring Cloud Discovery implementations are supported as well. There are examples using Consul and Zookeeper.
As with Spring Boot 2 most of the endpoints aren’t exposed via http by default, we expose all of them. For production you should carefully choose which endpoints to expose.
You can include the Spring Boot Admin Server to your Eureka server. Setup everything as described above and set spring.boot.admin.context-path to something different than "/" so that the Spring Boot Admin Server UI won’t clash with Eureka’s one.
+
+
+
+
+
+
+
+
+
Use of SNAPSHOT-Versions
+
+
+
If you want to use a snapshot version of Spring Boot Admin Server you most likely need to include the spring and sonatype snapshot repositories:
+
+
+
+
+
+
+
+
+
diff --git a/3.3.3/js/initializeHighlight.js b/3.3.3/js/initializeHighlight.js
new file mode 100644
index 00000000000..458d00661e2
--- /dev/null
+++ b/3.3.3/js/initializeHighlight.js
@@ -0,0 +1,14 @@
+/**
+ * Licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * Initializes highlight.js.
+ *
+ * This file can be overriden to change the initialization.
+ */
+
+/**
+ * Initializes highlight.js when the document loads.
+ */
+// Uses the default initialization
+hljs.initHighlightingOnLoad();
diff --git a/3.3.3/lib/bootstrap/dist/js/bootstrap.min.js b/3.3.3/lib/bootstrap/dist/js/bootstrap.min.js
new file mode 100644
index 00000000000..836e1768a26
--- /dev/null
+++ b/3.3.3/lib/bootstrap/dist/js/bootstrap.min.js
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v5.2.2 (https://getbootstrap.com/)
+ * Copyright 2011-2022 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){if(t&&t.__esModule)return 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="transitionend",n=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},o=t=>{const e=n(t);return e&&document.querySelector(e)?e:null},r=t=>{const e=n(t);return e?document.querySelector(e):null},a=t=>{t.dispatchEvent(new Event(s))},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(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,p=[],m=()=>"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?(p.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of p)t()})),p.push(e)):e()},v=t=>{"function"==typeof t&&t()},y=(t,e,i=!0)=>{if(!i)return void v(t);const n=(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 o=!1;const r=({target:i})=>{i===e&&(o=!0,e.removeEventListener(s,r),v(t))};e.addEventListener(s,r),setTimeout((()=>{o||a(e)}),n)},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 L={mouseenter:"mouseover",mouseleave:"mouseout"},O=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 I(t,e){return e&&`${e}::${k++}`||t.uidEvent||k++}function S(t){const e=I(t);return t.uidEvent=e,T[e]=T[e]||{},T[e]}function D(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function N(t,e,i){const s="string"==typeof e,n=s?i:e||i;let o=j(t);return O.has(o)||(o=t),[s,n,o]}function P(t,e,i,s,n){if("string"!=typeof e||!t)return;let[o,r,a]=N(e,i,s);if(e in L){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=S(t),c=l[a]||(l[a]={}),h=D(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&n);const d=I(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&&$.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&&$.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 x(t,e,i,s,n){const o=D(e[i],s,n);o&&(t.removeEventListener(i,o,Boolean(n)),delete e[i][o.uidEvent])}function M(t,e,i,s){const n=e[i]||{};for(const o of Object.keys(n))if(o.includes(s)){const s=n[o];x(t,e,i,s.callable,s.delegationSelector)}}function j(t){return t=t.replace(E,""),L[t]||t}const $={on(t,e,i,s){P(t,e,i,s,!1)},one(t,e,i,s){P(t,e,i,s,!0)},off(t,e,i,s){if("string"!=typeof e||!t)return;const[n,o,r]=N(e,i,s),a=r!==e,l=S(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))M(t,l,i,e.slice(1));for(const i of Object.keys(c)){const s=i.replace(C,"");if(!a||e.includes(s)){const e=c[i];x(t,l,r,e.callable,e.delegationSelector)}}}else{if(!Object.keys(c).length)return;x(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!==j(e)&&s&&(n=s.Event(e,i),s(t).trigger(n),o=!n.isPropagationStopped(),r=!n.isImmediatePropagationStopped(),a=n.isDefaultPrevented());let l=new Event(e,{bubbles:o,cancelable:!0});return l=F(l,i),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}const z=new Map,H={set(t,e,i){z.has(t)||z.set(t,new Map);const s=z.get(t);s.has(e)||0===s.size?s.set(e,i):console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(s.keys())[0]}.`)},get:(t,e)=>z.has(t)&&z.get(t).get(e)||null,remove(t,e){if(!z.has(t))return;const i=z.get(t);i.delete(e),0===i.size&&z.delete(t)}};function q(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 B(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const W={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${B(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${B(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]=q(t.dataset[s])}return e},getDataAttribute:(t,e)=>q(t.getAttribute(`data-bs-${B(e)}`))};class R{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)?W.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...l(e)?W.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const s of Object.keys(e)){const n=e[s],o=t[s],r=l(o)?"element":null==(i=o)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(n).test(r))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${s}" provided type "${r}" but expected type "${n}".`)}var i}}class V extends R{constructor(t,e){super(),(t=c(t))&&(this._element=t,this._config=this._getConfig(e),H.set(this._element,this.constructor.DATA_KEY,this))}dispose(){H.remove(this._element,this.constructor.DATA_KEY),$.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 H.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.2.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 K=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,s=t.NAME;$.on(document,i,`[data-bs-dismiss="${s}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),d(this))return;const n=r(this)||this.closest(`.${s}`);t.getOrCreateInstance(n)[e]()}))};class Q extends V{static get NAME(){return"alert"}close(){if($.trigger(this._element,"close.bs.alert").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(),$.trigger(this._element,"closed.bs.alert"),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Q.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)}}))}}K(Q,"close"),b(Q);const X='[data-bs-toggle="button"]';class Y extends V{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=Y.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}$.on(document,"click.bs.button.data-api",X,(t=>{t.preventDefault();const e=t.target.closest(X);Y.getOrCreateInstance(e).toggle()})),b(Y);const U={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)))}},G={endCallback:null,leftCallback:null,rightCallback:null},J={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class Z extends R{constructor(t,e){super(),this._element=t,t&&Z.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return G}static get DefaultType(){return J}static get NAME(){return"swipe"}dispose(){$.off(this._element,".bs.swipe")}_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?($.on(this._element,"pointerdown.bs.swipe",(t=>this._start(t))),$.on(this._element,"pointerup.bs.swipe",(t=>this._end(t))),this._element.classList.add("pointer-event")):($.on(this._element,"touchstart.bs.swipe",(t=>this._start(t))),$.on(this._element,"touchmove.bs.swipe",(t=>this._move(t))),$.on(this._element,"touchend.bs.swipe",(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 tt="next",et="prev",it="left",st="right",nt="slid.bs.carousel",ot="carousel",rt="active",at={ArrowLeft:st,ArrowRight:it},lt={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ct={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class ht extends V{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=U.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===ot&&this.cycle()}static get Default(){return lt}static get DefaultType(){return ct}static get NAME(){return"carousel"}next(){this._slide(tt)}nextWhenVisible(){!document.hidden&&h(this._element)&&this.next()}prev(){this._slide(et)}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?$.one(this._element,nt,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void $.one(this._element,nt,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const s=t>i?tt:et;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&&$.on(this._element,"keydown.bs.carousel",(t=>this._keydown(t))),"hover"===this._config.pause&&($.on(this._element,"mouseenter.bs.carousel",(()=>this.pause())),$.on(this._element,"mouseleave.bs.carousel",(()=>this._maybeEnableCycle()))),this._config.touch&&Z.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of U.find(".carousel-item img",this._element))$.on(t,"dragstart.bs.carousel",(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(it)),rightCallback:()=>this._slide(this._directionToOrder(st)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new Z(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=at[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=U.findOne(".active",this._indicatorsElement);e.classList.remove(rt),e.removeAttribute("aria-current");const i=U.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(rt),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===tt,n=e||w(this._getItems(),i,s,this._config.wrap);if(n===i)return;const o=this._getItemIndex(n),r=e=>$.trigger(this._element,e,{relatedTarget:n,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r("slide.bs.carousel").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(rt),i.classList.remove(rt,c,l),this._isSliding=!1,r(nt)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return U.findOne(".active.carousel-item",this._element)}_getItems(){return U.find(".carousel-item",this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return m()?t===it?et:tt:t===it?tt:et}_orderToDirection(t){return m()?t===et?it:st:t===et?st:it}static jQueryInterface(t){return this.each((function(){const e=ht.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)}))}}$.on(document,"click.bs.carousel.data-api","[data-bs-slide], [data-bs-slide-to]",(function(t){const e=r(this);if(!e||!e.classList.contains(ot))return;t.preventDefault();const i=ht.getOrCreateInstance(e),s=this.getAttribute("data-bs-slide-to");return s?(i.to(s),void i._maybeEnableCycle()):"next"===W.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),$.on(window,"load.bs.carousel.data-api",(()=>{const t=U.find('[data-bs-ride="carousel"]');for(const e of t)ht.getOrCreateInstance(e)})),b(ht);const dt="show",ut="collapse",_t="collapsing",gt='[data-bs-toggle="collapse"]',ft={parent:null,toggle:!0},pt={parent:"(null|element)",toggle:"boolean"};class mt extends V{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=U.find(gt);for(const t of i){const e=o(t),i=U.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 ft}static get DefaultType(){return pt}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=>mt.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if($.trigger(this._element,"show.bs.collapse").defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(ut),this._element.classList.add(_t),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(_t),this._element.classList.add(ut,dt),this._element.style[e]="",$.trigger(this._element,"shown.bs.collapse")}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if($.trigger(this._element,"hide.bs.collapse").defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,g(this._element),this._element.classList.add(_t),this._element.classList.remove(ut,dt);for(const t of this._triggerArray){const e=r(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(_t),this._element.classList.add(ut),$.trigger(this._element,"hidden.bs.collapse")}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(dt)}_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(gt);for(const e of t){const t=r(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=U.find(":scope .collapse .collapse",this._config.parent);return U.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=mt.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}$.on(document,"click.bs.collapse.data-api",gt,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();const e=o(this),i=U.find(e);for(const t of i)mt.getOrCreateInstance(t,{toggle:!1}).toggle()})),b(mt);const bt="dropdown",vt="ArrowUp",yt="ArrowDown",wt="click.bs.dropdown.data-api",At="keydown.bs.dropdown.data-api",Et="show",Ct='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',Tt=`${Ct}.show`,kt=".dropdown-menu",Lt=m()?"top-end":"top-start",Ot=m()?"top-start":"top-end",It=m()?"bottom-end":"bottom-start",St=m()?"bottom-start":"bottom-end",Dt=m()?"left-start":"right-start",Nt=m()?"right-start":"left-start",Pt={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},xt={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class Mt extends V{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=U.next(this._element,kt)[0]||U.prev(this._element,kt)[0]||U.findOne(kt,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Pt}static get DefaultType(){return xt}static get NAME(){return bt}toggle(){return this._isShown()?this.hide():this.show()}show(){if(d(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!$.trigger(this._element,"show.bs.dropdown",t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))$.on(t,"mouseover",_);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Et),this._element.classList.add(Et),$.trigger(this._element,"shown.bs.dropdown",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(!$.trigger(this._element,"hide.bs.dropdown",t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))$.off(t,"mouseover",_);this._popper&&this._popper.destroy(),this._menu.classList.remove(Et),this._element.classList.remove(Et),this._element.setAttribute("aria-expanded","false"),W.removeDataAttribute(this._menu,"popper"),$.trigger(this._element,"hidden.bs.dropdown",t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!l(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${bt.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(Et)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Dt;if(t.classList.contains("dropstart"))return Nt;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?Ot:Lt:e?St:It}_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)&&(W.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,..."function"==typeof this._config.popperConfig?this._config.popperConfig(t):this._config.popperConfig}}_selectMenuItem({key:t,target:e}){const i=U.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=Mt.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=U.find(Tt);for(const i of e){const e=Mt.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=[vt,yt].includes(t.key);if(!s&&!i)return;if(e&&!i)return;t.preventDefault();const n=this.matches(Ct)?this:U.prev(this,Ct)[0]||U.next(this,Ct)[0]||U.findOne(Ct,t.delegateTarget.parentNode),o=Mt.getOrCreateInstance(n);if(s)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),n.focus())}}$.on(document,At,Ct,Mt.dataApiKeydownHandler),$.on(document,At,kt,Mt.dataApiKeydownHandler),$.on(document,wt,Mt.clearMenus),$.on(document,"keyup.bs.dropdown.data-api",Mt.clearMenus),$.on(document,wt,Ct,(function(t){t.preventDefault(),Mt.getOrCreateInstance(this).toggle()})),b(Mt);const jt=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",$t=".sticky-top",Ft="padding-right",zt="margin-right";class Ht{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,Ft,(e=>e+t)),this._setElementAttributes(jt,Ft,(e=>e+t)),this._setElementAttributes($t,zt,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,Ft),this._resetElementAttributes(jt,Ft),this._resetElementAttributes($t,zt)}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&&W.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=W.getDataAttribute(t,e);null!==i?(W.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 U.find(t,this._element))e(i)}}const qt="show",Bt="mousedown.bs.backdrop",Wt={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Rt={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Vt extends R{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Wt}static get DefaultType(){return Rt}static get NAME(){return"backdrop"}show(t){if(!this._config.isVisible)return void v(t);this._append();const e=this._getElement();this._config.isAnimated&&g(e),e.classList.add(qt),this._emulateAnimation((()=>{v(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(qt),this._emulateAnimation((()=>{this.dispose(),v(t)}))):v(t)}dispose(){this._isAppended&&($.off(this._element,Bt),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),$.on(t,Bt,(()=>{v(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){y(t,this._getElement(),this._config.isAnimated)}}const Kt=".bs.focustrap",Qt="backward",Xt={autofocus:!0,trapElement:null},Yt={autofocus:"boolean",trapElement:"element"};class Ut extends R{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return Xt}static get DefaultType(){return Yt}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),$.off(document,Kt),$.on(document,"focusin.bs.focustrap",(t=>this._handleFocusin(t))),$.on(document,"keydown.tab.bs.focustrap",(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,$.off(document,Kt))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=U.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===Qt?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?Qt:"forward")}}const Gt="hidden.bs.modal",Jt="show.bs.modal",Zt="modal-open",te="show",ee="modal-static",ie={backdrop:!0,focus:!0,keyboard:!0},se={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class ne extends V{constructor(t,e){super(t,e),this._dialog=U.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new Ht,this._addEventListeners()}static get Default(){return ie}static get DefaultType(){return se}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||$.trigger(this._element,Jt,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Zt),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&($.trigger(this._element,"hide.bs.modal").defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(te),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){for(const t of[window,this._dialog])$.off(t,".bs.modal");this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Vt({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new Ut({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=U.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),g(this._element),this._element.classList.add(te),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,$.trigger(this._element,"shown.bs.modal",{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){$.on(this._element,"keydown.dismiss.bs.modal",(t=>{if("Escape"===t.key)return this._config.keyboard?(t.preventDefault(),void this.hide()):void this._triggerBackdropTransition()})),$.on(window,"resize.bs.modal",(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),$.on(this._element,"mousedown.dismiss.bs.modal",(t=>{$.one(this._element,"click.dismiss.bs.modal",(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(Zt),this._resetAdjustments(),this._scrollBar.reset(),$.trigger(this._element,Gt)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if($.trigger(this._element,"hidePrevented.bs.modal").defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(ee)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(ee),this._queueCallback((()=>{this._element.classList.remove(ee),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=m()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=m()?"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=ne.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}$.on(document,"click.bs.modal.data-api",'[data-bs-toggle="modal"]',(function(t){const e=r(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),$.one(e,Jt,(t=>{t.defaultPrevented||$.one(e,Gt,(()=>{h(this)&&this.focus()}))}));const i=U.findOne(".modal.show");i&&ne.getInstance(i).hide(),ne.getOrCreateInstance(e).toggle(this)})),K(ne),b(ne);const oe="show",re="showing",ae="hiding",le=".offcanvas.show",ce="hidePrevented.bs.offcanvas",he="hidden.bs.offcanvas",de={backdrop:!0,keyboard:!0,scroll:!1},ue={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class _e extends V{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return de}static get DefaultType(){return ue}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||$.trigger(this._element,"show.bs.offcanvas",{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new Ht).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(re),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(oe),this._element.classList.remove(re),$.trigger(this._element,"shown.bs.offcanvas",{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&($.trigger(this._element,"hide.bs.offcanvas").defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(ae),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(oe,ae),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new Ht).reset(),$.trigger(this._element,he)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Vt({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():$.trigger(this._element,ce)}:null})}_initializeFocusTrap(){return new Ut({trapElement:this._element})}_addEventListeners(){$.on(this._element,"keydown.dismiss.bs.offcanvas",(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():$.trigger(this._element,ce))}))}static jQueryInterface(t){return this.each((function(){const e=_e.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)}}))}}$.on(document,"click.bs.offcanvas.data-api",'[data-bs-toggle="offcanvas"]',(function(t){const e=r(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),d(this))return;$.one(e,he,(()=>{h(this)&&this.focus()}));const i=U.findOne(le);i&&i!==e&&_e.getInstance(i).hide(),_e.getOrCreateInstance(e).toggle(this)})),$.on(window,"load.bs.offcanvas.data-api",(()=>{for(const t of U.find(le))_e.getOrCreateInstance(t).show()})),$.on(window,"resize.bs.offcanvas",(()=>{for(const t of U.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&_e.getOrCreateInstance(t).hide()})),K(_e),b(_e);const ge=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),fe=/^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i,pe=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i,me=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!ge.has(i)||Boolean(fe.test(t.nodeValue)||pe.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},be={"*":["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:[]},ve={allowList:be,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:""},ye={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},we={entry:"(string|element|function|null)",selector:"(string|element)"};class Ae extends R{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return ve}static get DefaultType(){return ye}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},we)}_setContent(t,e,i){const s=U.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)me(e,n)||t.removeAttribute(e.nodeName)}return s.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return"function"==typeof t?t(this):t}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const Ee=new Set(["sanitize","allowList","sanitizeFn"]),Ce="fade",Te="show",ke=".modal",Le="hide.bs.modal",Oe="hover",Ie="focus",Se={AUTO:"auto",TOP:"top",RIGHT:m()?"left":"right",BOTTOM:"bottom",LEFT:m()?"right":"left"},De={allowList:be,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,0],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'
',title:"",trigger:"hover focus"},Ne={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 Pe extends V{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 De}static get DefaultType(){return Ne}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),$.off(this._element.closest(ke),Le,this._hideModalHandler),this.tip&&this.tip.remove(),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=$.trigger(this._element,this.constructor.eventName("show")),e=(u(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this.tip&&(this.tip.remove(),this.tip=null);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),$.trigger(this._element,this.constructor.eventName("inserted"))),this._popper?this._popper.update():this._popper=this._createPopper(i),i.classList.add(Te),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))$.on(t,"mouseover",_);this._queueCallback((()=>{$.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(!this._isShown())return;if($.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented)return;const t=this._getTipElement();if(t.classList.remove(Te),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))$.off(t,"mouseover",_);this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||t.remove(),this._element.removeAttribute("aria-describedby"),$.trigger(this._element,this.constructor.eventName("hidden")),this._disposePopper())}),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(Ce,Te),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(Ce),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new Ae({...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(Ce)}_isShown(){return this.tip&&this.tip.classList.contains(Te)}_createPopper(t){const e="function"==typeof this._config.placement?this._config.placement.call(this,t,this._element):this._config.placement,s=Se[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"function"==typeof t?t.call(this._element):t}_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,..."function"==typeof this._config.popperConfig?this._config.popperConfig(e):this._config.popperConfig}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)$.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===Oe?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===Oe?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");$.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?Ie:Oe]=!0,e._enter()})),$.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?Ie:Oe]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},$.on(this._element.closest(ke),Le,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=W.getDataAttributes(this._element);for(const t of Object.keys(e))Ee.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 in this._config)this.constructor.Default[e]!==this._config[e]&&(t[e]=this._config[e]);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null)}static jQueryInterface(t){return this.each((function(){const e=Pe.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}b(Pe);const xe={...Pe.Default,content:"",offset:[0,8],placement:"right",template:'
` tags are not being overridden anymore
+- More correct highlighting of code blocks inside non-`
` containers:
+ highlighter now doesn't insist on replacing them with its own container and
+ just replaces the contents.
+- Small fixes in browser compatibility and heuristics.
+
+[c++ 0x]: http://ru.wikipedia.org/wiki/C%2B%2B0x
+[html 5]: http://en.wikipedia.org/wiki/HTML5
+[ik]: http://kalnitsky.org.ua/
+
+### For developers
+
+The most significant change is the ability to include language submodes right
+under `contains` instead of defining explicit named submodes in the main array:
+
+ contains: [
+ 'string',
+ 'number',
+ {begin: '\\n', end: hljs.IMMEDIATE_RE}
+ ]
+
+This is useful for auxiliary modes needed only in one place to define parsing.
+Note that such modes often don't have `className` and hence won't generate a
+separate `` in the resulting markup. This is similar in effect to
+`noMarkup: true`. All existing languages have been refactored accordingly.
+
+Test file test.html has at last become a real test. Now it not only puts the
+detected language name under the code snippet but also tests if it matches the
+expected one. Test summary is displayed right above all language snippets.
+
+
+## CDN
+
+Fine people at [Yandex][] agreed to host highlight.js on their big fast servers.
+[Link up][l]!
+
+[yandex]: http://yandex.com/
+[l]: http://softwaremaniacs.org/soft/highlight/en/download/
+
+
+## Version 5.10 — "Paris".
+
+Though I'm on a vacation in Paris, I decided to release a new version with a
+couple of small fixes:
+
+- Tomas Vitvar discovered that TAB replacement doesn't always work when used
+ with custom markup in code
+- SQL parsing is even more rigid now and doesn't step over SmallTalk in tests
+
+
+## Version 5.9
+
+A long-awaited version is finally released.
+
+New languages:
+
+- Andrew Fedorov made a definition for Lua
+- a long-time highlight.js contributor [Peter Leonov][pl] made a definition for
+ Nginx config
+- [Vladimir Moskva][vm] made a definition for TeX
+
+[pl]: http://kung-fu-tzu.ru/
+[vm]: http://fulc.ru/
+
+Fixes for existing languages:
+
+- [Loren Segal][ls] reworked the Ruby definition and added highlighting for
+ [YARD][] inline documentation
+- the definition of SQL has become more solid and now it shouldn't be overly
+ greedy when it comes to language detection
+
+[ls]: http://gnuu.org/
+[yard]: http://yardoc.org/
+
+The highlighter has become more usable as a library allowing to do highlighting
+from initialization code of JS frameworks and in ajax methods (see.
+readme.eng.txt).
+
+Also this version drops support for the [WordPress][wp] plugin. Everyone is
+welcome to [pick up its maintenance][p] if needed.
+
+[wp]: http://wordpress.org/
+[p]: http://bazaar.launchpad.net/~isagalaev/+junk/highlight/annotate/342/src/wp_highlight.js.php
+
+
+## Version 5.8
+
+- Jan Berkel has contributed a definition for Scala. +1 to hotness!
+- All CSS-styles are rewritten to work only inside `
` tags to avoid
+ conflicts with host site styles.
+
+
+## Version 5.7.
+
+Fixed escaping of quotes in VBScript strings.
+
+
+## Version 5.5
+
+This version brings a small change: now .ini-files allow digits, underscores and
+square brackets in key names.
+
+
+## Version 5.4
+
+Fixed small but upsetting bug in the packer which caused incorrect highlighting
+of explicitly specified languages. Thanks to Andrew Fedorov for precise
+diagnostics!
+
+
+## Version 5.3
+
+The version to fulfil old promises.
+
+The most significant change is that highlight.js now preserves custom user
+markup in code along with its own highlighting markup. This means that now it's
+possible to use, say, links in code. Thanks to [Vladimir Dolzhenko][vd] for the
+[initial proposal][1] and for making a proof-of-concept patch.
+
+Also in this version:
+
+- [Vasily Polovnyov][vp] has sent a GitHub-like style and has implemented
+ support for CSS @-rules and Ruby symbols.
+- Yura Zaripov has sent two styles: Brown Paper and School Book.
+- Oleg Volchkov has sent a definition for [Parser 3][p3].
+
+[1]: http://softwaremaniacs.org/forum/highlightjs/6612/
+[p3]: http://www.parser.ru/
+[vp]: http://vasily.polovnyov.ru/
+[vd]: http://dolzhenko.blogspot.com/
+
+
+## Version 5.2
+
+- at last it's possible to replace indentation TABs with something sensible
+ (e.g. 2 or 4 spaces)
+- new keywords and built-ins for 1C by Sergey Baranov
+- a couple of small fixes to Apache highlighting
+
+
+## Version 5.1
+
+This is one of those nice version consisting entirely of new and shiny
+contributions!
+
+- [Vladimir Ermakov][vooon] created highlighting for AVR Assembler
+- [Ruslan Keba][rukeba] created highlighting for Apache config file. Also his
+ original visual style for it is now available for all highlight.js languages
+ under the name "Magula".
+- [Shuen-Huei Guan][drake] (aka Drake) sent new keywords for RenderMan
+ languages. Also thanks go to [Konstantin Evdokimenko][ke] for his advice on
+ the matter.
+
+[vooon]: http://vehq.ru/about/
+[rukeba]: http://rukeba.com/
+[drake]: http://drakeguan.org/
+[ke]: http://k-evdokimenko.moikrug.ru/
+
+
+## Version 5.0
+
+The main change in the new major version of highlight.js is a mechanism for
+packing several languages along with the library itself into a single compressed
+file. Now sites using several languages will load considerably faster because
+the library won't dynamically include additional files while loading.
+
+Also this version fixes a long-standing bug with Javascript highlighting that
+couldn't distinguish between regular expressions and division operations.
+
+And as usually there were a couple of minor correctness fixes.
+
+Great thanks to all contributors! Keep using highlight.js.
+
+
+## Version 4.3
+
+This version comes with two contributions from [Jason Diamond][jd]:
+
+- language definition for C# (yes! it was a long-missed thing!)
+- Visual Studio-like highlighting style
+
+Plus there are a couple of minor bug fixes for parsing HTML and XML attributes.
+
+[jd]: http://jason.diamond.name/weblog/
+
+
+## Version 4.2
+
+The biggest news is highlighting for Lisp, courtesy of Vasily Polovnyov. It's
+somewhat experimental meaning that for highlighting "keywords" it doesn't use
+any pre-defined set of a Lisp dialect. Instead it tries to highlight first word
+in parentheses wherever it makes sense. I'd like to ask people programming in
+Lisp to confirm if it's a good idea and send feedback to [the forum][f].
+
+Other changes:
+
+- Smalltalk was excluded from DEFAULT_LANGUAGES to save traffic
+- [Vladimir Epifanov][voldmar] has implemented javascript style switcher for
+ test.html
+- comments now allowed inside Ruby function definition
+- [MEL][] language from [Shuen-Huei Guan][drake]
+- whitespace now allowed between `
` and ``
+- better auto-detection of C++ and PHP
+- HTML allows embedded VBScript (`<% .. %>`)
+
+[f]: http://softwaremaniacs.org/forum/highlightjs/
+[voldmar]: http://voldmar.ya.ru/
+[mel]: http://en.wikipedia.org/wiki/Maya_Embedded_Language
+[drake]: http://drakeguan.org/
+
+
+## Version 4.1
+
+Languages:
+
+- Bash from Vah
+- DOS bat-files from Alexander Makarov (Sam)
+- Diff files from Vasily Polovnyov
+- Ini files from myself though initial idea was from Sam
+
+Styles:
+
+- Zenburn from Vladimir Epifanov, this is an imitation of a
+ [well-known theme for Vim][zenburn].
+- Ascetic from myself, as a realization of ideals of non-flashy highlighting:
+ just one color in only three gradations :-)
+
+In other news. [One small bug][bug] was fixed, built-in keywords were added for
+Python and C++ which improved auto-detection for the latter (it was shame that
+[my wife's blog][alenacpp] had issues with it from time to time). And lastly
+thanks go to Sam for getting rid of my stylistic comments in code that were
+getting in the way of [JSMin][].
+
+[zenburn]: http://en.wikipedia.org/wiki/Zenburn
+[alenacpp]: http://alenacpp.blogspot.com/
+[bug]: http://softwaremaniacs.org/forum/viewtopic.php?id=1823
+[jsmin]: http://code.google.com/p/jsmin-php/
+
+
+## Version 4.0
+
+New major version is a result of vast refactoring and of many contributions.
+
+Visible new features:
+
+- Highlighting of embedded languages. Currently is implemented highlighting of
+ Javascript and CSS inside HTML.
+- Bundled 5 ready-made style themes!
+
+Invisible new features:
+
+- Highlight.js no longer pollutes global namespace. Only one object and one
+ function for backward compatibility.
+- Performance is further increased by about 15%.
+
+Changing of a major version number caused by a new format of language definition
+files. If you use some third-party language files they should be updated.
+
+
+## Version 3.5
+
+A very nice version in my opinion fixing a number of small bugs and slightly
+increased speed in a couple of corner cases. Thanks to everybody who reports
+bugs in he [forum][f] and by email!
+
+There is also a new language — XML. A custom XML formerly was detected as HTML
+and didn't highlight custom tags. In this version I tried to make custom XML to
+be detected and highlighted by its own rules. Which by the way include such
+things as CDATA sections and processing instructions (` ... ?>`).
+
+[f]: http://softwaremaniacs.org/forum/viewforum.php?id=6
+
+
+## Version 3.3
+
+[Vladimir Gubarkov][xonix] has provided an interesting and useful addition.
+File export.html contains a little program that shows and allows to copy and
+paste an HTML code generated by the highlighter for any code snippet. This can
+be useful in situations when one can't use the script itself on a site.
+
+
+[xonix]: http://xonixx.blogspot.com/
+
+
+## Version 3.2 consists completely of contributions:
+
+- Vladimir Gubarkov has described SmallTalk
+- Yuri Ivanov has described 1C
+- Peter Leonov has packaged the highlighter as a Firefox extension
+- Vladimir Ermakov has compiled a mod for phpBB
+
+Many thanks to you all!
+
+
+## Version 3.1
+
+Three new languages are available: Django templates, SQL and Axapta. The latter
+two are sent by [Dmitri Roudakov][1]. However I've almost entirely rewrote an
+SQL definition but I'd never started it be it from the ground up :-)
+
+The engine itself has got a long awaited feature of grouping keywords
+("keyword", "built-in function", "literal"). No more hacks!
+
+[1]: http://roudakov.ru/
+
+
+## Version 3.0
+
+It is major mainly because now highlight.js has grown large and has become
+modular. Now when you pass it a list of languages to highlight it will
+dynamically load into a browser only those languages.
+
+Also:
+
+- Konstantin Evdokimenko of [RibKit][] project has created a highlighting for
+ RenderMan Shading Language and RenderMan Interface Bytestream. Yay for more
+ languages!
+- Heuristics for C++ and HTML got better.
+- I've implemented (at last) a correct handling of backslash escapes in C-like
+ languages.
+
+There is also a small backwards incompatible change in the new version. The
+function initHighlighting that was used to initialize highlighting instead of
+initHighlightingOnLoad a long time ago no longer works. If you by chance still
+use it — replace it with the new one.
+
+[RibKit]: http://ribkit.sourceforge.net/
+
+
+## Version 2.9
+
+Highlight.js is a parser, not just a couple of regular expressions. That said
+I'm glad to announce that in the new version 2.9 has support for:
+
+- in-string substitutions for Ruby -- `#{...}`
+- strings from from numeric symbol codes (like #XX) for Delphi
+
+
+## Version 2.8
+
+A maintenance release with more tuned heuristics. Fully backwards compatible.
+
+
+## Version 2.7
+
+- Nikita Ledyaev presents highlighting for VBScript, yay!
+- A couple of bugs with escaping in strings were fixed thanks to Mickle
+- Ongoing tuning of heuristics
+
+Fixed bugs were rather unpleasant so I encourage everyone to upgrade!
+
+
+## Version 2.4
+
+- Peter Leonov provides another improved highlighting for Perl
+- Javascript gets a new kind of keywords — "literals". These are the words
+ "true", "false" and "null"
+
+Also highlight.js homepage now lists sites that use the library. Feel free to
+add your site by [dropping me a message][mail] until I find the time to build a
+submit form.
+
+[mail]: mailto:Maniac@SoftwareManiacs.Org
+
+
+## Version 2.3
+
+This version fixes IE breakage in previous version. My apologies to all who have
+already downloaded that one!
+
+
+## Version 2.2
+
+- added highlighting for Javascript
+- at last fixed parsing of Delphi's escaped apostrophes in strings
+- in Ruby fixed highlighting of keywords 'def' and 'class', same for 'sub' in
+ Perl
+
+
+## Version 2.0
+
+- Ruby support by [Anton Kovalyov][ak]
+- speed increased by orders of magnitude due to new way of parsing
+- this same way allows now correct highlighting of keywords in some tricky
+ places (like keyword "End" at the end of Delphi classes)
+
+[ak]: http://anton.kovalyov.net/
+
+
+## Version 1.0
+
+Version 1.0 of javascript syntax highlighter is released!
+
+It's the first version available with English description. Feel free to post
+your comments and question to [highlight.js forum][forum]. And don't be afraid
+if you find there some fancy Cyrillic letters -- it's for Russian users too :-)
+
+[forum]: http://softwaremaniacs.org/forum/viewforum.php?id=6
diff --git a/3.3.3/lib/highlight/LICENSE b/3.3.3/lib/highlight/LICENSE
new file mode 100644
index 00000000000..422deb7350f
--- /dev/null
+++ b/3.3.3/lib/highlight/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2006, Ivan Sagalaev
+All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of highlight.js nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/3.3.3/lib/highlight/README.md b/3.3.3/lib/highlight/README.md
new file mode 100644
index 00000000000..9f76e6bd56e
--- /dev/null
+++ b/3.3.3/lib/highlight/README.md
@@ -0,0 +1,150 @@
+# Highlight.js
+
+[![Build Status](https://travis-ci.org/isagalaev/highlight.js.svg?branch=master)](https://travis-ci.org/isagalaev/highlight.js)
+
+Highlight.js is a syntax highlighter written in JavaScript. It works in
+the browser as well as on the server. It works with pretty much any
+markup, doesn’t depend on any framework and has automatic language
+detection.
+
+## Getting Started
+
+The bare minimum for using highlight.js on a web page is linking to the
+library along with one of the styles and calling
+[`initHighlightingOnLoad`][1]:
+
+```html
+
+
+
+```
+
+This will find and highlight code inside of `
` tags; it tries
+to detect the language automatically. If automatic detection doesn’t
+work for you, you can specify the language in the `class` attribute:
+
+```html
+
...
+```
+
+The list of supported language classes is available in the [class
+reference][2]. Classes can also be prefixed with either `language-` or
+`lang-`.
+
+To disable highlighting altogether use the `nohighlight` class:
+
+```html
+
...
+```
+
+## Custom Initialization
+
+When you need a bit more control over the initialization of
+highlight.js, you can use the [`highlightBlock`][3] and [`configure`][4]
+functions. This allows you to control *what* to highlight and *when*.
+
+Here’s an equivalent way to calling [`initHighlightingOnLoad`][1] using
+jQuery:
+
+```javascript
+$(document).ready(function() {
+ $('pre code').each(function(i, block) {
+ hljs.highlightBlock(block);
+ });
+});
+```
+
+You can use any tags instead of `
` to mark up your code. If
+you don't use a container that preserve line breaks you will need to
+configure highlight.js to use the ` ` tag:
+
+```javascript
+hljs.configure({useBR: true});
+
+$('div.code').each(function(i, block) {
+ hljs.highlightBlock(block);
+});
+```
+
+For other options refer to the documentation for [`configure`][4].
+
+
+## Web Workers
+
+You can run highlighting inside a web worker to avoid freezing the browser
+window while dealing with very big chunks of code.
+
+In your main script:
+
+```javascript
+addEventListener('load', function() {
+ var code = document.querySelector('#code');
+ var worker = new Worker('worker.js');
+ worker.onmessage = function(event) { code.innerHTML = event.data; }
+ worker.postMessage(code.textContent);
+})
+```
+
+In worker.js:
+
+```javascript
+onmessage = function(event) {
+ importScripts('/highlight.pack.js');
+ var result = self.hljs.highlightAuto(event.data);
+ postMessage(result.value);
+}
+```
+
+
+## Getting the Library
+
+You can get highlight.js as a hosted, or custom-build, browser script or
+as a server module. Right out of the box the browser script supports
+both AMD and CommonJS, so if you wish you can use RequireJS or
+Browserify without having to build from source. The server module also
+works perfectly fine with Browserify, but there is the option to use a
+build specific to browsers rather than something meant for a server.
+Head over to the [download page][5] for all the options.
+
+**Don't link to GitHub directly.** The library is not supposed to work straight
+from the source, it requires building. If none of the pre-packaged options
+work for you refer to the [building documentation][6].
+
+**The CDN-hosted package doesn't have all the languages.** Otherwise it'd be
+too big. If you don't see the language you need in the ["Common" section][5],
+it can be added manually:
+
+```html
+
+```
+
+**On Almond.** You need to use the optimizer to give the module a name. For
+example:
+
+```
+r.js -o name=hljs paths.hljs=/path/to/highlight out=highlight.js
+```
+
+
+## License
+
+Highlight.js is released under the BSD License. See [LICENSE][7] file
+for details.
+
+## Links
+
+The official site for the library is at .
+
+Further in-depth documentation for the API and other topics is at
+.
+
+Authors and contributors are listed in the [AUTHORS.en.txt][8] file.
+
+[1]: http://highlightjs.readthedocs.io/en/latest/api.html#inithighlightingonload
+[2]: http://highlightjs.readthedocs.io/en/latest/css-classes-reference.html
+[3]: http://highlightjs.readthedocs.io/en/latest/api.html#highlightblock-block
+[4]: http://highlightjs.readthedocs.io/en/latest/api.html#configure-options
+[5]: https://highlightjs.org/download/
+[6]: http://highlightjs.readthedocs.io/en/latest/building-testing.html
+[7]: https://github.com/isagalaev/highlight.js/blob/master/LICENSE
+[8]: https://github.com/isagalaev/highlight.js/blob/master/AUTHORS.en.txt
diff --git a/3.3.3/lib/highlight/README.ru.md b/3.3.3/lib/highlight/README.ru.md
new file mode 100644
index 00000000000..ac481d07181
--- /dev/null
+++ b/3.3.3/lib/highlight/README.ru.md
@@ -0,0 +1,142 @@
+# Highlight.js
+
+Highlight.js — это инструмент для подсветки синтаксиса, написанный на JavaScript. Он работает
+и в браузере, и на сервере. Он работает с практически любой HTML разметкой, не
+зависит от каких-либо фреймворков и умеет автоматически определять язык.
+
+
+## Начало работы
+
+Минимум, что нужно сделать для использования highlight.js на веб-странице — это
+подключить библиотеку, CSS-стили и вызывать [`initHighlightingOnLoad`][1]:
+
+```html
+
+
+
+```
+
+Библиотека найдёт и раскрасит код внутри тегов `
`, попытавшись
+автоматически определить язык. Когда автоопределение не срабатывает, можно явно
+указать язык в атрибуте class:
+
+```html
+
...
+```
+
+Список поддерживаемых классов языков доступен в [справочнике по классам][2].
+Класс также можно предварить префиксами `language-` или `lang-`.
+
+Чтобы отключить подсветку для какого-то блока, используйте класс `nohighlight`:
+
+```html
+
...
+```
+
+## Инициализация вручную
+
+Чтобы иметь чуть больше контроля за инициализацией подсветки, вы можете
+использовать функции [`highlightBlock`][3] и [`configure`][4]. Таким образом
+можно управлять тем, *что* и *когда* подсвечивать.
+
+Вот пример инициализации, эквивалентной вызову [`initHighlightingOnLoad`][1], но
+с использованием jQuery:
+
+```javascript
+$(document).ready(function() {
+ $('pre code').each(function(i, block) {
+ hljs.highlightBlock(block);
+ });
+});
+```
+
+Вы можете использовать любые теги разметки вместо `
Die Verantwortliche im Sinne der Datenschutz-Grundverordnung und anderer nationaler Datenschutzgesetze der Mitgliedsstaaten sowie sonstiger datenschutzrechtlicher Bestimmungen ist die:
+
+
+
codecentric AG Hochstraße 11 42697 Solingen Deutschland Tel.: +49 [0] 212 23 36 28 0 E-Mail: office@codecentric.de Website: www.codecentric.de
+
+
+
+
Anschrift des Datenschutzbeauftragten
+
+
Der Datenschutzbeauftragte der Verantwortlichen ist wie folgt zu erreichen:
Wir verarbeiten personenbezogene Daten unserer Nutzer grundsätzlich nur, soweit dies zur Bereitstellung einer funktionsfähigen Website sowie unserer Inhalte und Leistungen erforderlich ist. Die Verarbeitung personenbezogener Daten unserer Nutzer erfolgt regelmäßig nur nach Einwilligung des Nutzers. Eine Ausnahme gilt in solchen Fällen, in denen eine vorherige Einholung einer Einwilligung aus tatsächlichen Gründen nicht möglich ist und die Verarbeitung der Daten durch gesetzliche Vorschriften gestattet ist.
+
+
+
+
Rechtsgrundlage für die Verarbeitung personenbezogener Daten
+
+
Soweit wir für Verarbeitungsvorgänge personenbezogener Daten eine Einwilligung der betroffenen Person einholen, dient Art. 6 Abs. 1 lit. a EU-Datenschutzgrundverordnung (DSGVO) als Rechtsgrundlage.
+
+
+
Bei der Verarbeitung von personenbezogenen Daten, die zur Erfüllung eines Vertrages, dessen Vertragspartei die betroffene Person ist, erforderlich ist, dient Art. 6 Abs. 1 lit. b DSGVO als Rechtsgrundlage. Dies gilt auch für Verarbeitungsvorgänge, die zur Durchführung vorvertraglicher Maßnahmen erforderlich sind.
+
+
+
Soweit eine Verarbeitung personenbezogener Daten zur Erfüllung einer rechtlichen Verpflichtung erforderlich ist, der unser Unternehmen unterliegt, dient Art. 6 Abs. 1 lit. c DSGVO als Rechtsgrundlage.
+
+
+
Für den Fall, dass lebenswichtige Interessen der betroffenen Person oder einer anderen natürlichen Person eine Verarbeitung personenbezogener Daten erforderlich machen, dient Art. 6 Abs. 1 lit. d DSGVO als Rechtsgrundlage.
+
+
+
Ist die Verarbeitung zur Wahrung eines berechtigten Interesses unseres Unternehmens oder eines Dritten erforderlich und überwiegen die Interessen, Grundrechte und Grundfreiheiten des Betroffenen das erstgenannte Interesse nicht, so dient Art. 6 Abs. 1 lit. f DSGVO als Rechtsgrundlage für die Verarbeitung.
+
+
+
+
Datenlöschung und Speicherdauer
+
+
Die personenbezogenen Daten der betroffenen Person werden gelöscht oder gesperrt, sobald der Zweck der Speicherung entfällt. Eine Speicherung kann darüber hinaus erfolgen, wenn dies durch den europäischen oder nationalen Gesetzgeber in unionsrechtlichen Verordnungen, Gesetzen oder sonstigen Vorschriften, denen der Verantwortliche unterliegt, vorgesehen wurde. Eine Sperrung oder Löschung der Daten erfolgt auch dann, wenn eine durch die genannten Normen vorgeschriebene Speicherfrist abläuft, es sei denn, dass eine Erforderlichkeit zur weiteren Speicherung der Daten für einen Vertragsabschluss oder eine Vertragserfüllung besteht.
+
+
+
+
+
Bereitstellung der Website und Erstellung von Logfiles
+
+
Beschreibung und Umfang der Datenverarbeitung
+
+
Bei jedem Aufruf unserer Internetseite erfasst unser System automatisiert Daten und Informationen vom Computersystem des aufrufenden Rechners.
+
+
+
Folgende Daten werden hierbei erhoben:
+
+
+
+
Informationen über den Browsertyp und die verwendete Version
+
Das Betriebssystem des Nutzers
+
Den Internet-Service-Provider des Nutzers
+
Die IP-Adresse des Nutzers
+
Datum und Uhrzeit des Zugriffs
+
Websites, von denen das System des Nutzers auf unsere Internetseite gelangt
+
Websites, die vom System des Nutzers über unsere Website aufgerufen werden
+
+
+
+
Die Daten werden ebenfalls in den Logfiles unseres Systems gespeichert. Eine Speicherung dieser Daten zusammen mit anderen personenbezogenen Daten des Nutzers findet nicht statt.
+
+
+
+
Rechtsgrundlage für die Datenverarbeitung
+
+
Rechtsgrundlage für die vorübergehende Speicherung der Daten und der Logfiles ist Art. 6 Abs. 1 lit. f DSGVO.
+
+
+
+
Zweck der Datenverarbeitung
+
+
Die vorübergehende Speicherung der IP-Adresse durch das System ist notwendig, um eine Auslieferung der Website an den Rechner des Nutzers zu ermöglichen. Hierfür muss die IP-Adresse des Nutzers für die Dauer der Sitzung gespeichert bleiben.
+
+
+
Die Speicherung in Logfiles erfolgt, um die Funktionsfähigkeit der Website sicherzustellen. Zudem dienen uns die Daten zur technischen Optimierung der Website und zur Sicherstellung der Sicherheit unserer informationstechnischen Systeme. Eine Auswertung der Daten zu Marketingzwecken findet in diesem Zusammenhang nicht statt.
+
+
+
In diesen Zwecken liegt auch unser berechtigtes Interesse an der Datenverarbeitung nach Art. 6 Abs. 1 lit. f DSGVO.
+
+
+
+
Dauer der Speicherung
+
+
Die Daten werden gelöscht, sobald sie für die Erreichung des Zweckes ihrer Erhebung nicht mehr erforderlich sind. Im Falle der Erfassung der Daten zur Bereitstellung der Website ist dies der Fall, wenn die jeweilige Sitzung beendet ist.
+
+
+
Im Falle der Speicherung der Daten in Logfiles ist dies nach spätestens sieben Tagen der Fall. Eine darüberhinausgehende Speicherung ist möglich. In diesem Fall werden die IP-Adressen der Nutzer gelöscht oder verfremdet, sodass eine Zuordnung des aufrufenden Clients nicht mehr möglich ist.
+
+
+
+
Widerspruchs- und Beseitigungsmöglichkeit
+
+
Die Erfassung der Daten zur Bereitstellung der Website und die Speicherung der Daten in Logfiles ist für den Betrieb der Internetseite zwingend erforderlich. Es besteht folglich seitens des Nutzers keine Widerspruchsmöglichkeit.
+
+
+
+
+
Rechte der betroffenen Person
+
+
Werden personenbezogene Daten von Ihnen verarbeitet, sind Sie Betroffener i.S.d. DSGVO und es stehen Ihnen folgende Rechte gegenüber dem Verantwortlichen zu:
+
+
+
Auskunftsrecht
+
+
Sie können von dem Verantwortlichen eine Bestätigung darüber verlangen, ob personenbezogene Daten, die Sie betreffen, von uns verarbeitet werden.
+
+
+
Liegt eine solche Verarbeitung vor, können Sie von dem Verantwortlichen über folgende Informationen Auskunft verlangen:
+
+
+
(1) die Zwecke, zu denen die personenbezogenen Daten verarbeitet werden;
+
+
+
(2) die Kategorien von personenbezogenen Daten, welche verarbeitet werden;
+
+
+
(3) die Empfänger bzw. die Kategorien von Empfängern, gegenüber denen die Sie betreffenden personenbezogenen Daten offengelegt wurden oder noch offengelegt werden;
+
+
+
(4) die geplante Dauer der Speicherung der Sie betreffenden personenbezogenen Daten oder, falls konkrete Angaben hierzu nicht möglich sind, Kriterien für die Festlegung der Speicherdauer;
+
+
+
(5) das Bestehen eines Rechts auf Berichtigung oder Löschung der Sie betreffenden personenbezogenen Daten, eines Rechts auf Einschränkung der Verarbeitung durch den Verantwortlichen oder eines Widerspruchsrechts gegen diese Verarbeitung;
+
+
+
(6) das Bestehen eines Beschwerderechts bei einer Aufsichtsbehörde;
+
+
+
(7) alle verfügbaren Informationen über die Herkunft der Daten, wenn die personenbezogenen Daten nicht bei der betroffenen Person erhoben werden;
+
+
+
(8) das Bestehen einer automatisierten Entscheidungsfindung einschließlich Profiling gemäß Art. 22 Abs. 1 und 4 DSGVO und – zumindest in diesen Fällen – aussagekräftige Informationen über die involvierte Logik sowie die Tragweite und die angestrebten Auswirkungen einer derartigen Verarbeitung für die betroffene Person.
+
+
+
Ihnen steht das Recht zu, Auskunft darüber zu verlangen, ob die Sie betreffenden personenbezogenen Daten in ein Drittland oder an eine internationale Organisation übermittelt werden. In diesem Zusammenhang können Sie verlangen, über die geeigneten Garantien gem. Art. 46 DSGVO im Zusammenhang mit der Übermittlung unterrichtet zu werden.
+
+
+
+
Recht auf Berichtigung
+
+
Sie haben ein Recht auf Berichtigung und/oder Vervollständigung gegenüber dem Verantwortlichen, sofern die verarbeiteten personenbezogenen Daten, die Sie betreffen, unrichtig oder unvollständig sind. Der Verantwortliche hat die Berichtigung unverzüglich vorzunehmen.
+
+
+
+
Recht auf Einschränkung der Verarbeitung
+
+
Unter den folgenden Voraussetzungen können Sie die Einschränkung der Verarbeitung der Sie betreffenden personenbezogenen Daten verlangen:
+
+
+
(1) wenn Sie die Richtigkeit der Sie betreffenden personenbezogenen für eine Dauer bestreiten, die es dem Verantwortlichen ermöglicht, die Richtigkeit der personenbezogenen Daten zu überprüfen;
+
+
+
(2) die Verarbeitung unrechtmäßig ist und Sie die Löschung der personenbezogenen Daten ablehnen und stattdessen die Einschränkung der Nutzung der personenbezogenen Daten verlangen;
+
+
+
(3) der Verantwortliche die personenbezogenen Daten für die Zwecke der Verarbeitung nicht länger benötigt, Sie diese jedoch zur Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen benötigen, oder
+
+
+
(4) wenn Sie Widerspruch gegen die Verarbeitung gemäß Art. 21 Abs. 1 DSGVO eingelegt haben und noch nicht feststeht, ob die berechtigten Gründe des Verantwortlichen gegenüber Ihren Gründen überwiegen.
+
+
+
Wurde die Verarbeitung der Sie betreffenden personenbezogenen Daten eingeschränkt, dürfen diese Daten – von ihrer Speicherung abgesehen – nur mit Ihrer Einwilligung oder zur Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen oder zum Schutz der Rechte einer anderen natürlichen oder juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der Union oder eines Mitgliedstaats verarbeitet werden.
+
+
+
Wurde die Einschränkung der Verarbeitung nach den o.g. Voraussetzungen eingeschränkt, werden Sie von dem Verantwortlichen unterrichtet, bevor die Einschränkung aufgehoben wird.
+
+
+
+
Recht auf Löschung
+
+
Löschungspflicht
+
+
Sie können von dem Verantwortlichen verlangen, dass die Sie betreffenden personenbezogenen Daten unverzüglich gelöscht werden, und der Verantwortliche ist verpflichtet, diese Daten unverzüglich zu löschen, sofern einer der folgenden Gründe zutrifft:
+
+
+
(1) Die Sie betreffenden personenbezogenen Daten sind für die Zwecke, für die sie erhoben oder auf sonstige Weise verarbeitet wurden, nicht mehr notwendig.
+
+
+
(2) Sie widerrufen Ihre Einwilligung, auf die sich die Verarbeitung gem. Art. 6 Abs. 1 lit. a oder Art. 9 Abs. 2 lit. a DSGVO stützte, und es fehlt an einer anderweitigen Rechtsgrundlage für die Verarbeitung.
+
+
+
(3) Sie legen gem. Art. 21 Abs. 1 DSGVO Widerspruch gegen die Verarbeitung ein und es liegen keine vorrangigen berechtigten Gründe für die Verarbeitung vor, oder Sie legen gem. Art. 21 Abs. 2 DSGVO Widerspruch gegen die Verarbeitung ein.
+
+
+
(4) Die Sie betreffenden personenbezogenen Daten wurden unrechtmäßig verarbeitet.
+
+
+
(5) Die Löschung der Sie betreffenden personenbezogenen Daten ist zur Erfüllung einer rechtlichen Verpflichtung nach dem Unionsrecht oder dem Recht der Mitgliedstaaten erforderlich, dem der Verantwortliche unterliegt.
+
+
+
(6) Die Sie betreffenden personenbezogenen Daten wurden in Bezug auf angebotene Dienste der Informationsgesellschaft gemäß Art. 8 Abs. 1 DSGVO erhoben.
+
+
+
+
Information an Dritte
+
+
Hat der Verantwortliche die Sie betreffenden personenbezogenen Daten öffentlich gemacht und ist er gem. Art. 17 Abs. 1 DSGVO zu deren Löschung verpflichtet, so trifft er unter Berücksichtigung der verfügbaren Technologie und der Implementierungskosten angemessene Maßnahmen, auch technischer Art, um für die Datenverarbeitung Verantwortliche, die die personenbezogenen Daten verarbeiten, darüber zu informieren, dass Sie als betroffene Person von ihnen die Löschung aller Links zu diesen personenbezogenen Daten oder von Kopien oder Replikationen dieser personenbezogenen Daten verlangt haben.
+
+
+
+
Ausnahmen
+
+
Das Recht auf Löschung besteht nicht, soweit die Verarbeitung erforderlich ist
+
+
+
(1) zur Ausübung des Rechts auf freie Meinungsäußerung und Information;
+
+
+
(2) zur Erfüllung einer rechtlichen Verpflichtung, die die Verarbeitung nach dem Recht der Union oder der Mitgliedstaaten, dem der Verantwortliche unterliegt, erfordert, oder zur Wahrnehmung einer Aufgabe, die im öffentlichen Interesse liegt oder in Ausübung öffentlicher Gewalt erfolgt, die dem Verantwortlichen übertragen wurde;
+
+
+
(3) aus Gründen des öffentlichen Interesses im Bereich der öffentlichen Gesundheit gemäß Art. 9 Abs. 2 lit. h und i sowie Art. 9 Abs. 3 DSGVO;
+
+
+
(4) für im öffentlichen Interesse liegende Archivzwecke, wissenschaftliche oder historische Forschungszwecke oder für statistische Zwecke gem. Art. 89 Abs. 1 DSGVO, soweit das unter Abschnitt a) genannte Recht voraussichtlich die Verwirklichung der Ziele dieser Verarbeitung unmöglich macht oder ernsthaft beeinträchtigt, oder
+
+
+
(5) zur Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen.
+
+
+
+
+
Recht auf Unterrichtung
+
+
Haben Sie das Recht auf Berichtigung, Löschung oder Einschränkung der Verarbeitung gegenüber dem Verantwortlichen geltend gemacht, ist dieser verpflichtet, allen Empfängern, denen die Sie betreffenden personenbezogenen Daten offengelegt wurden, diese Berichtigung oder Löschung der Daten oder Einschränkung der Verarbeitung mitzuteilen, es sei denn, dies erweist sich als unmöglich oder ist mit einem unverhältnismäßigen Aufwand verbunden.
+
+
+
Ihnen steht gegenüber dem Verantwortlichen das Recht zu, über diese Empfänger unterrichtet zu werden.
+
+
+
+
Recht auf Datenübertragbarkeit
+
+
Sie haben das Recht, die Sie betreffenden personenbezogenen Daten, die Sie dem Verantwortlichen bereitgestellt haben, in einem strukturierten, gängigen und maschinenlesbaren Format zu erhalten. Außerdem haben Sie das Recht diese Daten einem anderen Verantwortlichen ohne Behinderung durch den Verantwortlichen, dem die personenbezogenen Daten bereitgestellt wurden, zu übermitteln, sofern
+
+
+
(1) die Verarbeitung auf einer Einwilligung gem. Art. 6 Abs. 1 lit. a DSGVO oder Art. 9 Abs. 2 lit. a DSGVO oder auf einem Vertrag gem. Art. 6 Abs. 1 lit. b DSGVO beruht und
+
+
+
(2) die Verarbeitung mithilfe automatisierter Verfahren erfolgt.
+
+
+
In Ausübung dieses Rechts haben Sie ferner das Recht, zu erwirken, dass die Sie betreffenden personenbezogenen Daten direkt von einem Verantwortlichen einem anderen Verantwortlichen übermittelt werden, soweit dies technisch machbar ist. Freiheiten und Rechte anderer Personen dürfen hierdurch nicht beeinträchtigt werden.
+
+
+
Das Recht auf Datenübertragbarkeit gilt nicht für eine Verarbeitung personenbezogener Daten, die für die Wahrnehmung einer Aufgabe erforderlich ist, die im öffentlichen Interesse liegt oder in Ausübung öffentlicher Gewalt erfolgt, die dem Verantwortlichen übertragen wurde.
+
+
+
+
Widerspruchsrecht
+
+
Sie haben das Recht, aus Gründen, die sich aus ihrer besonderen Situation ergeben, jederzeit gegen die Verarbeitung der Sie betreffenden personenbezogenen Daten, die aufgrund von Art. 6 Abs. 1 lit. e oder f DSGVO erfolgt, Widerspruch einzulegen; dies gilt auch für ein auf diese Bestimmungen gestütztes Profiling.
+
+
+
Der Verantwortliche verarbeitet die Sie betreffenden personenbezogenen Daten nicht mehr, es sei denn, er kann zwingende schutzwürdige Gründe für die Verarbeitung nachweisen, die Ihre Interessen, Rechte und Freiheiten überwiegen, oder die Verarbeitung dient der Geltendmachung, Ausübung oder Verteidigung von Rechtsansprüchen.
+
+
+
Werden die Sie betreffenden personenbezogenen Daten verarbeitet, um Direktwerbung zu betreiben, haben Sie das Recht, jederzeit Widerspruch gegen die Verarbeitung der Sie betreffenden personenbezogenen Daten zum Zwecke derartiger Werbung einzulegen; dies gilt auch für das Profiling, soweit es mit solcher Direktwerbung in Verbindung steht.
+
+
+
Widersprechen Sie der Verarbeitung für Zwecke der Direktwerbung, so werden die Sie betreffenden personenbezogenen Daten nicht mehr für diese Zwecke verarbeitet.
+
+
+
Sie haben die Möglichkeit, im Zusammenhang mit der Nutzung von Diensten der Informationsgesellschaft – ungeachtet der Richtlinie 2002/58/EG – Ihr Widerspruchsrecht mittels automatisierter Verfahren auszuüben, bei denen technische Spezifikationen verwendet werden.
+
+
+
+
Recht auf Widerruf der datenschutzrechtlichen Einwilligungserklärung
+
+
Sie haben das Recht, Ihre datenschutzrechtliche Einwilligungserklärung jederzeit zu widerrufen. Durch den Widerruf der Einwilligung wird die Rechtmäßigkeit der aufgrund der Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt.
+
+
+
+
Automatisierte Entscheidung im Einzelfall einschließlich Profiling
+
+
Sie haben das Recht, nicht einer ausschließlich auf einer automatisierten Verarbeitung – einschließlich Profiling – beruhenden Entscheidung unterworfen zu werden, die Ihnen gegenüber rechtliche Wirkung entfaltet oder Sie in ähnlicher Weise erheblich beeinträchtigt. Dies gilt nicht, wenn die Entscheidung
+
+
+
(1) für den Abschluss oder die Erfüllung eines Vertrags zwischen Ihnen und dem Verantwortlichen erforderlich ist,
+
+
+
(2) aufgrund von Rechtsvorschriften der Union oder der Mitgliedstaaten, denen der Verantwortliche unterliegt, zulässig ist und diese Rechtsvorschriften angemessene Maßnahmen zur Wahrung Ihrer Rechte und Freiheiten sowie Ihrer berechtigten Interessen enthalten oder
+
+
+
(3) mit Ihrer ausdrücklichen Einwilligung erfolgt.
+
+
+
Allerdings dürfen diese Entscheidungen nicht auf besonderen Kategorien personenbezogener Daten nach Art. 9 Abs. 1 DSGVO beruhen, sofern nicht Art. 9 Abs. 2 lit. a oder g DSGVO gilt und angemessene Maßnahmen zum Schutz der Rechte und Freiheiten sowie Ihrer berechtigten Interessen getroffen wurden.
+
+
+
Hinsichtlich der in (1) und (3) genannten Fälle trifft der Verantwortliche angemessene Maßnahmen, um die Rechte und Freiheiten sowie Ihre berechtigten Interessen zu wahren, wozu mindestens das Recht auf Erwirkung des Eingreifens einer Person seitens des Verantwortlichen, auf Darlegung des eigenen Standpunkts und auf Anfechtung der Entscheidung gehört.
+
+
+
+
Recht auf Beschwerde bei einer Aufsichtsbehörde
+
+
Unbeschadet eines anderweitigen verwaltungsrechtlichen oder gerichtlichen Rechtsbehelfs steht Ihnen das Recht auf Beschwerde bei einer Aufsichtsbehörde, insbesondere in dem Mitgliedstaat ihres Aufenthaltsorts, ihres Arbeitsplatzes oder des Orts des mutmaßlichen Verstoßes, zu, wenn Sie der Ansicht sind, dass die Verarbeitung der Sie betreffenden personenbezogenen Daten gegen die DSGVO verstößt.
+
+
+
Die Aufsichtsbehörde, bei der die Beschwerde eingereicht wurde, unterrichtet den Beschwerdeführer über den Stand und die Ergebnisse der Beschwerde einschließlich der Möglichkeit eines gerichtlichen Rechtsbehelfs nach Art. 78 DSGVO.
You can easily integrate Spring Boot Admin with Flask or FastAPI Python applications using the Pyctuator project.
+
+
+
The following steps uses Flask, but other web frameworks are supported as well. See Pyctuator’s documentation for an updated list of supported frameworks and features.
+
+
+
+
Install the pyctuator package:
+
+
+
pip install pyctuator
+
+
+
Enable pyctuator by pointing it to your Flask app and letting it know where Spring Boot Admin is running:
Since there are several approaches on solving authentication and authorization in distributed web applications Spring Boot Admin doesn’t ship a default one. By default spring-boot-admin-server-ui provides a login page and a logout button.
+
+
+
A Spring Security configuration for your server could look like this:
If you protect the /instances endpoint don’t forget to configure the username and password on your SBA-Client using spring.boot.admin.client.username and spring.boot.admin.client.password.
+
+
+
+
+
+
+
Securing Client Actuator Endpoints
+
+
When the actuator endpoints are secured using HTTP Basic authentication the SBA Server needs credentials to access them. You can submit the credentials in the metadata when registering the application. The BasicAuthHttpHeaderProvider then uses this metadata to add the Authorization header to access your application’s actuator endpoints. You can provide your own HttpHeadersProvider to alter the behaviour (e.g. add some decryption) or add extra headers.
+
+
+
+
+
+
+
The SBA Server masks certain metadata in the HTTP interface to prevent leaking of sensitive information.
+
+
+
+
+
+
+
+
+
+
You should configure HTTPS for your SBA Server or (service registry) when submitting credentials via the metadata.
+
+
+
+
+
+
+
+
+
+
When using Spring Cloud Discovery, you must be aware that anybody who can query your service registry can obtain the credentials.
+
+
+
+
+
+
+
+
+
+
When using this approach the SBA Server decides whether or not the user can access the registered applications. There are more complex solutions possible (using OAuth2) to let the clients decide if the user can access the endpoints. For that please have a look at the samples in joshiste/spring-boot-admin-samples.
To enable pulling credentials from properties the spring.boot.admin.instance-auth.enabled property must be true (default).
+
+
+
+
+
+
+
If your clients provide credentials via metadata (i.e., via service annotations), that metadata will be used instead of the properites.
+
+
+
+
+
+
You can provide a default username and password by setting spring.boot.admin.instance-auth.default-user-name and spring.boot.admin.instance-auth.default-user-password. Optionally you can provide credentials for specific services (by name) using the spring.boot.admin.instance-auth.service-map.*.user-name pattern, replacing * with the service name.
Consul does not allow dots (".") in metadata keys, use dashes instead.
+
+
+
+
+
+
+
CSRF on Actuator Endpoints
+
+
Some of the actuator endpoints (e.g. /loggers) support POST requests. When using Spring Security you need to ignore the actuator endpoints for CSRF-Protection as the Spring Boot Admin Server currently lacks support.
SBA Server can also use client certificates to authenticate when accessing the actuator endpoints. If a custom configured ClientHttpConnector bean is present, Spring Boot will automatically configure a WebClient.Builder using it, which will be used by Spring Boot Admin.
Mail notifications will be delivered as HTML emails rendered using Thymeleaf templates. To enable Mail notifications, configure a JavaMailSender using spring-boot-starter-mail and set a recipient.
+
+
+
+
+
+
+
+
+ Figure 1. Sample Mail Notification
+
+
+
+
+
+
+
+
To prevent disclosure of sensitive information, the default mail template doesn’t show any metadata of the instance. If you want to you show some of the metadata you can use a custom template.
+
+
+
+
+
+
+
Add spring-boot-starter-mail to your dependencies:
Additional properties which can be accessed from the template
+
+
+
+
+
+
+
+
+
PagerDuty Notifications
+
+
To enable PagerDuty notifications you just have to add a generic service to your PagerDuty-account and set spring.boot.admin.notify.pagerduty.service-key to the service-key you received.
Description to use in the event. SpEL-expressions are supported
+
"#{instance.registration.name}/#{instance.id} is #{instance.statusInfo.status}"
+
+
+
spring.boot.admin.notify.pagerduty.client
+
Client-name to use in the event
+
+
+
+
spring.boot.admin.notify.pagerduty.client-url
+
Client-url to use in the event
+
+
+
+
+
+
+
OpsGenie Notifications
+
+
To enable OpsGenie notifications you just have to add a new JSON Rest API integration to your OpsGenie account and set spring.boot.admin.notify.opsgenie.api-key to the apiKey you received.
Subtitle of the Activity section of the Teams message when an app changes status. SpEL-expressions are supported.
+
#{instance.registration.name} with id #{instance.id} changed status from #{lastStatus} to #{event.statusInfo.status}
+
+
+
+
+
+
Telegram Notifications
+
+
To enable Telegram notifications you need to create and authorize a telegram bot and set the appropriate configuration properties for auth-token and chat-id.
The proxy username (if proxy requires authentication)
+
+
+
+
spring.boot.admin.notify.proxy.password
+
The proxy password (if proxy requires authentication)
+
+
+
+
+
+
+
Notification Reminder
+
+
The RemindingNotifier sends reminders for down/offline applications, it delegates the sending of notifications to another notifier.
+
+
+
By default, a reminder is triggered when a registered application changes to DOWN or OFFLINE. You can alter this behaviour via setReminderStatuses(). The reminder ends when either the status changes to a non-triggering status or the regarding application gets deregistered.
+
+
+
By default, the reminders are sent every 10 minutes, to change this use setReminderPeriod(). The RemindingNotifier itself doesn’t start the background thread to send the reminders, you need to take care of this as shown in the given example below;
Schedules sending of due reminders every 10 seconds.
+
+
+
+
+
+
+
Filtering Notifications
+
+
The FilteringNotifier allows you to filter certain notification based on rules you can add/remove at runtime. It delegates the sending of notifications to another notifier.
+
+
+
If you add a FilteringNotifier to your ApplicationContext a RESTful interface on notifications/filter gets available. The restful interface provides the following methods for getting, adding, and deleting notification filters:
+
+
+
+
GET notifications/filter
+
+
+
Returns a list of all registered notification filters. Each containing the attributes id, applicationName, expiry, and expired.
+
+
+
POST notifications/filters?instanceId=<yourInstanceId>&applicationName=<yourApplicationName>&ttl=<yourInstant>
+
+
+
Posts a new notification filter for the application/instance of the given instanceId or applicationName. Either instanceId or applicationName must be set. The parameter ttl is optional and represents the expiration of the filter as an instant (the number of seconds from the epoch of 1970-01-01T00:00:00Z).
+
+
+
DELETE notifications/filters/{id}
+
+
+
Deletes the notification filter with the requested id from the filters.
+
+
+
+
+
+
You may as well access all notification filter configurations via the main applications view inside SBA client, as seen in the screenshot below.
+
+
+
+
+
+
+
+
+
+
A FilteringNotifier might be useful, for instance, if you don’t want to receive notifications when deploying your applications. Before stopping the application, you can add an (expiring) filter via a POST request.
Add the FilteringNotifier bean using a delegate (e.g. MailNotifier when configured)
+
+
+
2
+
Add the RemindingNotifier as primary bean using the FilteringNotifier as delegate.
+
+
+
+
+
+
+
+
+
+
This example combines the reminding and filtering notifiers. This allows you to get notifications after the deployed application hasn’t restarted in a certain amount of time (until the filter expires).
+
+
+
+
+
+
+
DingTalk Notifications
+
+
To enable DingTalk notifications you need to create and authorize a dingtalk bot and set the appropriate configuration properties for webhookUrl and secret.
In case the Spring Boot Admin server is running behind a reverse proxy, it may be requried to configure the public url where the server is reachable via (spring.boot.admin.ui.public-url). In addition when the reverse proxy terminates the https connection, it may be necessary to configure server.forward-headers-strategy=native (also see Spring Boot Reference Guide).
+
+
+
+
+
Configuration Options
+
+
+
+
+
+
+
+
+
+
Property name
+
Description
+
Default value
+
+
+
+
+
spring.boot.admin.server.enabled
+
Enables the Spring Boot Admin Server.
+
true
+
+
+
spring.boot.admin.context-path
+
The context-path prefixes the path where the Admin Server’s statics assets and API should be served. Relative to the Dispatcher-Servlet.
+
+
+
+
spring.boot.admin.monitor.status-interval
+
Time interval to check the status of instances.
+
10,000ms
+
+
+
spring.boot.admin.monitor.status-max-backoff
+
The maximal backoff for status check retries (retry after error has exponential backoff, minimum backoff is 1 second).
+
60,000ms
+
+
+
spring.boot.admin.monitor.status-lifetime
+
Lifetime of status. The status won’t be updated as long the last status isn’t expired.
+
10,000ms
+
+
+
spring.boot.admin.monitor.info-interval
+
Time interval to check the info of instances.
+
1m
+
+
+
spring.boot.admin.monitor.info-max-backoff
+
The maximal backoff for info check retries (retry after error has exponential backoff, minimum backoff is 1 second).
+
10m
+
+
+
spring.boot.admin.monitor.info-lifetime
+
Lifetime of info. The info won’t be updated as long the last info isn’t expired.
+
1m
+
+
+
spring.boot.admin.monitor.default-timeout
+
Default timeout when making requests. Individual values for specific endpoints can be overridden using spring.boot.admin.monitor.timeout.*. However, for interval based tasks like statusUpdate (i.e. HealthCheck) there are some limitations: the default-timeout cannot be longer than the interval. If so, the specified value of the interval is used as timeout.
+
10,000
+
+
+
spring.boot.admin.monitor.timeout.*
+
Key-Value-Pairs with the timeout per endpointId. Defaults to default-timeout.
+
+
+
+
spring.boot.admin.monitor.default-retries
+
Default number of retries for failed requests. Modifying requests (PUT, POST, PATCH, DELETE) are never retried. Individual values for specific endpoints can be overridden using spring.boot.admin.monitor.retries.*.
+
0
+
+
+
spring.boot.admin.monitor.retries.*
+
Key-Value-Pairs with the number of retries per endpointId. Defaults to default-retries. Modifying requests (PUT, POST, PATCH, DELETE) are never retried.
+
+
+
+
spring.boot.admin.metadata-keys-to-sanitize
+
Metadata values for the keys matching these regex patterns will be sanitized in all json output.
Starting from Spring Boot 3, all actuator values are masked by default.
Take a look at the Spring Boot documentation in order to configure unsanitizing of values (Sanitize Sensitive Values).
For Spring Boot 1.x client applications SBA probes for the specified endpoints using an OPTIONS request. If the path differs from the id you can specify this as id:path (e.g. health:ping)..
A user password used to authenticate to the registered service with the specified name. The spring.boot.admin.instance-auth.enabled property must be true.
+
+
+
+
spring.boot.admin.instance-proxy.ignored-headers
+
Headers not to be forwarded when making requests to clients.
+
"Cookie", "Set-Cookie", "Authorization"
+
+
+
spring.boot.admin.ui.public-url
+
Base url to use to build the base href in the ui.
+
If running behind a reverse proxy (using path rewriting) this can be used to make correct self references. If the host/port is omitted it will be inferred from the request.
Icon used as default favicon and icon for desktop notifications.
+
"assets/img/favicon.png"
+
+
+
spring.boot.admin.ui.favicon-danger
+
Icon used as favicon when one or more service is down and for desktop notifications.
+
"assets/img/favicon-danger.png"
+
+
+
spring.boot.admin.ui.remember-me-enabled
+
Switch to show/hide the remember-me checkbox on the login page.
+
true
+
+
+
spring.boot.admin.ui.poll-timer.cache
+
Polling duration in ms to fetch new cache data.
+
2500
+
+
+
spring.boot.admin.ui.poll-timer.datasource
+
Polling duration in ms to fetch new datasource data.
+
2500
+
+
+
spring.boot.admin.ui.poll-timer.gc
+
Polling duration in ms to fetch new gc data.
+
2500
+
+
+
spring.boot.admin.ui.poll-timer.process
+
Polling duration in ms to fetch new process data.
+
2500
+
+
+
spring.boot.admin.ui.poll-timer.memory
+
Polling duration in ms to fetch new memory data.
+
2500
+
+
+
spring.boot.admin.ui.poll-timer.threads
+
Polling duration in ms to fetch new threads data.
+
2500
+
+
+
spring.boot.admin.ui.poll-timer.logfile
+
Polling duration in ms to fetch new logfile data.
+
1000
+
+
+
spring.boot.admin.ui.enable-toasts
+
Allows to enable toast notifications.
+
false
+
+
+
+
+
+
+
Spring Cloud Discovery
+
+
+
The Spring Boot Admin Server can use Spring Clouds DiscoveryClient to discover applications. The advantage is that the clients don’t have to include the spring-boot-admin-starter-client. You just have to add a DiscoveryClient implementation to your admin server - everything else is done by AutoConfiguration.
+
+
+
Static Configuration using SimpleDiscoveryClient
+
+
Spring Cloud provides a SimpleDiscoveryClient. It allows you to specify client applications via static configuration:
Spring Boot Admin supports all other implementations of Spring Cloud’s DiscoveryClient (Eureka, Zookeeper, Consul, Kubernetes, …). You need to add it to the Spring Boot Admin Server and configure it properly. An example setup using Eureka is shown above.
+
+
+
+
Converting ServiceInstances
+
+
The information from the service registry are converted by the ServiceInstanceConverter. Spring Boot Admin ships with a default and Eureka converter implementation. The correct one is selected by AutoConfiguration.
+
+
+
+
+
+
+
You can modify how the information from the registry is used to register the application by using SBA Server configuration options and instance metadata. The values from the metadata takes precedence over the server config. If the plenty of options don’t fit your needs you can provide your own ServiceInstanceConverter.
+
+
+
+
+
+
+
+
+
+
When using Eureka, the healthCheckUrl known to Eureka is used for health-checking, which can be set on your client using eureka.instance.healthCheckUrl.
+
+
+
+
+
+
+ Table 1. Instance metadata options
+
+
+
+
+
+
+
+
+
Key
+
Value
+
Default value
+
+
+
+
+
user.name user.password
+
Credentials being used to access the endpoints.
+
+
+
+
management.scheme
+
The scheme is substituted in the service URL and will be used for accessing the actuator endpoints.
+
+
+
+
management.address
+
The address is substituted in the service URL and will be used for accessing the actuator endpoints.
+
+
+
+
management.port
+
The port is substituted in the service URL and will be used for accessing the actuator endpoints.
+
+
+
+
management.context-path
+
The path is appended to the service URL and will be used for accessing the actuator endpoints.
Instances of services will be ignored if they contain at least one metadata item that matches this list. (e.g. "discoverable=false")
+
+
+
+
spring.boot.admin.discovery.instances-metadata
+
Instances of services will be included if they contain at least one metadata item that matches this list. (e.g. "discoverable=true")
+
+
+
+
+
+
+
CloudFoundry
+
+
If you are deploying your applications to CloudFoundry then vcap.application.application_id and vcap.application.instance_indexmust be added to the metadata for proper registration of applications with Spring Boot Admin Server. Here is a sample configuration for Eureka:
Spring Boot Admin Server supports cluster replication via Hazelcast. It is automatically enabled when a HazelcastConfig- or HazelcastInstance-Bean is present. You can also configure the Hazelcast instance to be persistent, to keep the status over restarts. Also have a look at the Spring Boot support for Hazelcast.
@Bean
+public Config hazelcastConfig() {
+ // This map is used to store the events.
+ // It should be configured to reliably hold all the data,
+ // Spring Boot Admin will compact the events, if there are too many
+ MapConfig eventStoreMap = new MapConfig(DEFAULT_NAME_EVENT_STORE_MAP).setInMemoryFormat(InMemoryFormat.OBJECT)
+ .setBackupCount(1)
+ .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100));
+
+ // This map is used to deduplicate the notifications.
+ // If data in this map gets lost it should not be a big issue as it will atmost
+ // lead to
+ // the same notification to be sent by multiple instances
+ MapConfig sentNotificationsMap = new MapConfig(DEFAULT_NAME_SENT_NOTIFICATIONS_MAP)
+ .setInMemoryFormat(InMemoryFormat.OBJECT)
+ .setBackupCount(1)
+ .setEvictionConfig(
+ new EvictionConfig().setEvictionPolicy(EvictionPolicy.LRU).setMaxSizePolicy(MaxSizePolicy.PER_NODE))
+ .setMergePolicyConfig(new MergePolicyConfig(PutIfAbsentMergePolicy.class.getName(), 100));
+
+ Config config = new Config();
+ config.addMapConfig(eventStoreMap);
+ config.addMapConfig(sentNotificationsMap);
+ config.setProperty("hazelcast.jmx", "true");
+
+ // WARNING: This setups a local cluster, you change it to fit your needs.
+ config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
+ TcpIpConfig tcpIpConfig = config.getNetworkConfig().getJoin().getTcpIpConfig();
+ tcpIpConfig.setEnabled(true);
+ tcpIpConfig.setMembers(singletonList("127.0.0.1"));
+ return config;
+}
+
+
+
+
+
+
+ Table 3. Hazelcast configuration options
+
+
+
+
+
+
+
+
+
Property name
+
Description
+
Default value
+
+
+
+
+
spring.boot.admin.hazelcast.enabled
+
Enables the Hazelcast support
+
true
+
+
+
spring.boot.admin.hazelcast.event-store
+
Name of the Hazelcast-map to store the events
+
"spring-boot-admin-event-store"
+
+
+
spring.boot.admin.hazelcast.sent-notifications
+
Name of the Hazelcast-map used to deduplicate the notifications.