diff --git a/.github/generate-docs/config.json b/.github/generate-docs/config.json new file mode 100644 index 0000000..1033456 --- /dev/null +++ b/.github/generate-docs/config.json @@ -0,0 +1,10 @@ +{ + "show_toc": false, + "examples_as_yaml": "true", + "custom_template_path": ".github/generate-docs/templates/github/base.md", + "template_md_options": { + "badge_as_image": true, + "show_heading_numbers": false, + "show_array_restrictions": false + } +} diff --git a/.github/generate-docs/generate.sh b/.github/generate-docs/generate.sh new file mode 100755 index 0000000..69e961f --- /dev/null +++ b/.github/generate-docs/generate.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Clear existing documentation +rm -f docs/variables/*.md + +# Generate variable documentation +generate-schema-doc --config-file ./.github/generate-docs/config.json defaults/schemas docs/variables/ diff --git a/.github/generate-docs/templates/github/base.md b/.github/generate-docs/templates/github/base.md new file mode 100644 index 0000000..523476b --- /dev/null +++ b/.github/generate-docs/templates/github/base.md @@ -0,0 +1,16 @@ +{% set depth = 0 %} +{{ schema.keywords.get("title").literal | default("Schema Docs") | md_heading(depth) }} +{% set contentBase %} +{% with schema=schema, skip_headers=False, depth=depth %} + {% include "content.md" %} +{% endwith %} +{% endset %} + +{{ md_get_toc() }} + +{{ contentBase }} + +---------------------------------------------------------------------------------------------------------------------------- +{% if config.with_footer -%} +Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans){% if config.footer_show_time %} on {{ get_local_time() }}{% endif %} +{%- endif -%} diff --git a/.github/generate-docs/templates/github/breadcrumbs.md b/.github/generate-docs/templates/github/breadcrumbs.md new file mode 100644 index 0000000..3dbd74b --- /dev/null +++ b/.github/generate-docs/templates/github/breadcrumbs.md @@ -0,0 +1,9 @@ +{% filter md_escape_for_table %} +{%- if config.show_breadcrumbs %} + {%- for node in schema.nodes_from_root -%} + {{ node.name_for_breadcrumbs }}{%- if not loop.last %} > {% endif -%} + {%- endfor -%} +{%- else -%} + {{- schema.property_name -}} +{% endif %} +{% endfilter %} diff --git a/.github/generate-docs/templates/github/content.md b/.github/generate-docs/templates/github/content.md new file mode 100644 index 0000000..a5ba34c --- /dev/null +++ b/.github/generate-docs/templates/github/content.md @@ -0,0 +1,92 @@ +{# + content is a template and not a macro in md + because macro parameters are not through context + when rendering a template from the macro and it caused + serious problems when using recursive calls + mandatory context parameters: + schema +#} +{# context parameters default values #} +{% set skip_headers = skip_headers or False %} +{% set depth = depth or 0 %} +{# end context parameters #} + +{% set keys = schema.keywords %} +{%- if not skip_headers %} + +{% if schema.title and schema.title | length > 0 %} +**Title:** {{ schema.title }} +{% endif %} + +{% set description = (schema | get_description) %} +{% include "section_description.md" %} +{% endif %} + +{% if schema.should_be_a_link(config) %} +{% elif schema.refers_to -%} + {%- with schema=schema.refers_to_merged, skip_headers=True, depth=depth -%} + {% include "content.md" %} + {% endwith %} +{% else %} + {# Display examples #} + {% set examples = schema.examples %} + {% if examples %} + {% include "section_examples.md" %} + {% endif %} + + {# Properties, pattern properties, additional properties #} + {% if schema.type_name == "object" %} + {{- schema | md_properties_table | md_generate_table -}} + {% endif %} + + {# Combining: allOf, anyOf, oneOf, not #} + {% if schema.kw_all_of %} + {% with operator="allOf", title="All of(Requirement)", current_node=schema.kw_all_of, skip_required=True %} + {% include "tabbed_section.md" %} + {% endwith %} + {% endif %} + {% if schema.kw_any_of %} + {% with operator="anyOf", title="Any of(Option)", current_node=schema.kw_any_of, skip_required=True %} + {% include "tabbed_section.md" %} + {% endwith %} + {% endif %} + {% if schema.kw_one_of %} + {% with operator="oneOf", title="One of(Option)",current_node=schema.kw_one_of, skip_required=True %} + {% include "tabbed_section.md" %} + {% endwith %} + {% endif %} + {% if schema.kw_not %} + {% include "section_not.md" %} + {% endif %} + + {# Enum and const #} + {% if schema.kw_enum -%} + {% include "section_one_of.md" %} + {%- endif %} + {%- if schema.kw_const -%} + Specific value: `{{ schema.kw_const.raw | python_to_json }}` + {%- endif -%} + + {# Conditional subschema, or if-then-else section #} + {% if schema.has_conditional %} + {% with skip_headers=False, depth=depth+1 %} + {% include "section_conditional_subschema.md" %} + {% endwith %} + {% endif %} + + {# Required properties that are not defined under "properties". They will only be listed #} + {% include "section_undocumented_required_properties.md" %} + + {# Show the requested type(s) #} + {{- schema | md_restrictions_table | md_generate_table -}} + + {# Show array restrictions #} + {% if schema.type_name.startswith("array") %} + {% include "section_array.md" %} + {% endif %} + + {# details of Properties, pattern properties, additional properties #} + {% if schema.type_name == "object" %} + {% include "section_properties_details.md" %} + {% endif %} +{% endif %} diff --git a/.github/generate-docs/templates/github/section_array.md b/.github/generate-docs/templates/github/section_array.md new file mode 100644 index 0000000..d634686 --- /dev/null +++ b/.github/generate-docs/templates/github/section_array.md @@ -0,0 +1,21 @@ +{{ schema | md_array_restrictions | md_generate_table }} + +{% if schema.kw_items %} +{{ schema | md_array_items_restrictions | md_generate_table }} + +{% for item in schema.kw_items %} + {% filter md_heading(depth+1) %} + {% with schema=item %}{%- include "breadcrumbs.md" %}{% endwith %} + {% endfilter %} + {% with schema=item, skip_headers=False, depth=depth+1, skip_required=True %} + {% include "content.md" %} + {% endwith %} +{% endfor %} +{% endif %} + +{% if schema.kw_contains and schema.kw_contains.literal != {} %} +{{ "At least one of the items must be" | md_heading(depth+1) }} +{% with schema=schema.kw_contains, skip_headers=False, depth=depth+1, skip_required=True %} + {% include "content.md" %} +{% endwith %} +{% endif %} diff --git a/.github/generate-docs/templates/github/section_conditional_subschema.md b/.github/generate-docs/templates/github/section_conditional_subschema.md new file mode 100644 index 0000000..fdcc2c4 --- /dev/null +++ b/.github/generate-docs/templates/github/section_conditional_subschema.md @@ -0,0 +1,24 @@ +{% if schema.kw_if %} + {% set first_property = schema.kw_if | get_first_property %} + + {% if schema.kw_then %} + {%- filter md_heading(depth) -%}If ( + {{- first_property.property_name | md_escape_for_table -}} + {{- " = " -}} + {{- first_property.kw_const.literal | python_to_json -}} + ){%- endfilter -%} + {% with schema=schema.kw_then, skip_headers=False, depth=depth %} + {% include "content.md" %} + {% endwith %} + {% endif %} + {% if schema.kw_else %} + {%- filter md_heading(depth) -%}Else (i.e. {{ " " }} + {{- first_property.property_name | md_escape_for_table -}} + {{- " != " -}} + {{- first_property.kw_const.literal | python_to_json -}} + ){%- endfilter -%} + {% with schema=schema.kw_else, skip_headers=False, depth=depth %} + {% include "content.md" %} + {% endwith %} + {% endif %} +{% endif %} diff --git a/.github/generate-docs/templates/github/section_description.md b/.github/generate-docs/templates/github/section_description.md new file mode 100644 index 0000000..c5988b7 --- /dev/null +++ b/.github/generate-docs/templates/github/section_description.md @@ -0,0 +1,4 @@ +{# Display description #} +{% if description %} +{{ description }} +{% endif %} diff --git a/.github/generate-docs/templates/github/section_examples.md b/.github/generate-docs/templates/github/section_examples.md new file mode 100644 index 0000000..d52018d --- /dev/null +++ b/.github/generate-docs/templates/github/section_examples.md @@ -0,0 +1,16 @@ +**Example{% if examples|length > 1 %}s{% endif %}:**{{ " " }} + +{% for example in examples %} + {%- if loop.first %}{{ "\n" }}{% endif -%} + {% set example_id = schema.html_id ~ "_ex" ~ loop.index %} + {%- if not examples_as_yaml -%} + {{- "" }}```json + {{- "\n" }}{{ example }} + {{- "\n" }}``` + {%- else -%} + {{- "" }}```yaml + {{- "\n" }}{{ example | yaml_example }} + {{- "\n" }}``` + {%- endif -%} + {{ "\n" }} +{% endfor %} diff --git a/.github/generate-docs/templates/github/section_not.md b/.github/generate-docs/templates/github/section_not.md new file mode 100644 index 0000000..b5be138 --- /dev/null +++ b/.github/generate-docs/templates/github/section_not.md @@ -0,0 +1,4 @@ +{{ "Must **not** be" | md_heading(depth+1) }} +{% with schema=schema.kw_not, skip_headers=False, depth=depth+1, skip_required=True %} + {% include "content.md" %} +{% endwith %} diff --git a/.github/generate-docs/templates/github/section_one_of.md b/.github/generate-docs/templates/github/section_one_of.md new file mode 100644 index 0000000..1303bcd --- /dev/null +++ b/.github/generate-docs/templates/github/section_one_of.md @@ -0,0 +1,4 @@ +Must be one of: +{% for enum_choice in schema.kw_enum.array_items %} +* {{ enum_choice.literal | python_to_json }} +{% endfor %} diff --git a/.github/generate-docs/templates/github/section_properties_details.md b/.github/generate-docs/templates/github/section_properties_details.md new file mode 100644 index 0000000..c0554a3 --- /dev/null +++ b/.github/generate-docs/templates/github/section_properties_details.md @@ -0,0 +1,32 @@ +{% for sub_property in schema.iterate_properties %} + {%- if sub_property.is_additional_properties and not sub_property.is_additional_properties_schema -%} + {% continue %} + {% endif %} + + {% set html_id = sub_property.html_id %} + + {% set description = sub_property | get_description %} + + {% filter md_heading(depth + 1, html_id) -%} + {%- filter replace('\n', '') -%} + {%- if not skip_required and sub_property.property_name -%} + {{ md_badge("Required", "blue") if sub_property.is_required_property else md_badge("Optional", "yellow") -}} + {%- endif -%} + {%- if sub_property is deprecated -%}~~{%- endif -%} + {%- if sub_property.is_pattern_property %}Pattern{% endif %} `{% with schema=sub_property %}{%- include "breadcrumbs.md" %}{% endwith %}` + {%- if sub_property is deprecated -%}~~{%- endif -%} + {%- endfilter %} + {%- endfilter %} + + {% if sub_property.is_pattern_property %} +> All property whose name matches the regular expression +```{{ sub_property.property_name }}``` ([Test](https://regex101.com/?regex={{ sub_property.property_name | urlencode }})) +must respect the following conditions + {% endif %} + + + {% with schema=sub_property, skip_headers=False, depth=depth+1 %} + {% include "content.md" %} + {% endwith %} + +{% endfor %} diff --git a/.github/generate-docs/templates/github/section_undocumented_required_properties.md b/.github/generate-docs/templates/github/section_undocumented_required_properties.md new file mode 100644 index 0000000..0c0a677 --- /dev/null +++ b/.github/generate-docs/templates/github/section_undocumented_required_properties.md @@ -0,0 +1,7 @@ +{% set undocumented_required_properties = schema | get_undocumented_required_properties %} +{% if undocumented_required_properties%} +{{ "The following properties are required" | md_heading(depth+1) }} +{% for required_property in undocumented_required_properties %} +* {{ required_property }} +{% endfor %} +{% endif %} diff --git a/.github/generate-docs/templates/github/tabbed_section.md b/.github/generate-docs/templates/github/tabbed_section.md new file mode 100644 index 0000000..a1d3dea --- /dev/null +++ b/.github/generate-docs/templates/github/tabbed_section.md @@ -0,0 +1,11 @@ + +{{ current_node | md_array_items(title) | md_generate_table }} + +{% for node in current_node.array_items %} + {% filter md_heading(depth+1, node.html_id) -%} + {% if node.is_pattern_property %}Pattern{% endif %} `{% with schema=node %}{%- include "breadcrumbs.md" %}{% endwith %}` + {%- endfilter %} + {% with schema=node, skip_headers=False, depth=depth+1 %} + {% include "content.md" %} + {% endwith %} +{% endfor %} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e48a04..7c829ca 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,8 @@ --- fail_fast: false +exclude: ^(docs/variables) + repos: - repo: https://github.com/adrienverge/yamllint rev: v1.26.3 diff --git a/README.md b/README.md index b4cebd5..0522bd1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Ansible role for setting up a VyOS router ## Release notes -Please see [Releases](https://github.com/bjw-s/ansible-role-vyos/releases) for information +Please see [Releases](https://github.com/bjw-s/ansible-role-vyos/releases) for information about the contents of each release. ## Requirements @@ -15,7 +15,11 @@ You can install dependencies using the requirements.txt file in this repository: This role has been tested against the following VyOS versions: - - 1.4 (Rolling) + - 1.4 (vyos-1.4-rolling-202111181848) + +## Documentation + +More documentation can be found [here](https://bjw-s.github.io/ansible-role-vyos/) ## License diff --git a/defaults/main/containers.yml b/defaults/main/containers.yml index e69de29..fb6e035 100644 --- a/defaults/main/containers.yml +++ b/defaults/main/containers.yml @@ -0,0 +1,9 @@ +--- +# ----------------------- +# Container configuration +# ----------------------- + +vyos_container: + registries: [] + networks: {} + containers: {} diff --git a/defaults/main/dns.yml b/defaults/main/dns.yml index fc64b56..99c6771 100644 --- a/defaults/main/dns.yml +++ b/defaults/main/dns.yml @@ -9,6 +9,7 @@ vyos_coredns: interfaces: [] config_path: /config/coredns container: + name: vyos-coredns repository: ghcr.io/k8s-at-home/coredns tag: v1.8.4 k8s_gateway: diff --git a/defaults/main/post_config.yml b/defaults/main/post_config.yml index 12ef437..cafc2c0 100644 --- a/defaults/main/post_config.yml +++ b/defaults/main/post_config.yml @@ -6,5 +6,3 @@ vyos_save_config: true vyos_run_post_config_script: true - -vyos_containers: {} diff --git a/defaults/schemas/vyos_container.schema.json b/defaults/schemas/vyos_container.schema.json new file mode 100644 index 0000000..eafab54 --- /dev/null +++ b/defaults/schemas/vyos_container.schema.json @@ -0,0 +1,243 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "vyos_container.schema.json", + "title": "vyos_container", + "description": "VyOS Container configuration", + "type": "object", + "properties": { + "registries": { + "description": "Unqualified search registries for any image that does not include the registry in the image name.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, + "networks": { + "description": "Container networks to be managed", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/networkItem" + } + }, + "containers": { + "description": "Container to be managed", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/containerItem" + } + } + }, + "additionalProperties": false, + "$defs": { + "networkItem": { + "type": "object", + "title": "Network entry", + "allOf": [ + { + "title": "Base fields", + "properties": { + "description": { + "description": "A string with a functional description of the network.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "title": "Network entry fields", + "oneOf": [ + { + "title": "Network entry with network reference", + "properties": { + "network": { + "description": "A reference to the key of an entry in the VyOS networks list. The prefix will be determined automatically.", + "type": "string" + } + }, + "required": ["network"], + "additionalProperties": false + }, + { + "title": "Network entry with custom prefix", + "properties": { + "prefix": { + "description": "A network prefix to be assigned to this container network.", + "type": "string" + } + }, + "required": ["prefix"], + "additionalProperties": false + } + ] + } + ], + "additionalProperties": false, + "examples": [ + { + "network": "services" + }, + { + "description": "test", + "network": "services" + }, + { + "description": "test", + "prefix": "10.35.0.0/24" + } + ] + }, + + "containerItem": { + "type": "object", + "title": "Container entry", + "allOf": [ + { + "title": "Base fields", + "properties": { + "image": { + "description": "Configures the container image.", + "type": "object", + "properties": { + "repository": { + "description": "The repository of the container image.", + "type": "string" + }, + "tag": { + "description": "The tag of the container image.", + "type": "string" + } + }, + "required": ["repository", "tag"], + "additionalProperties": false + }, + "cap-add": { + "description": "A list of capabilities to be added to the container.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true, + "examples": [ + { + "cap-add": ["net-admin"] + } + ] + }, + "env": { + "description": "A dict containing the environment variables to be set in the container.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "ports": { + "description": "A dict containing the ports to be configured in the container.", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "source": { + "description": "The source port on the host.", + "type": "integer" + }, + "destination": { + "description": "The destination port on the container.", + "type": "integer" + } + }, + "required": ["source", "destination"], + "additionalProperties": false + } + }, + "volumes": { + "description": "A dict containing the volumes to be configured in the container.", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "source": { + "description": "The source port on the host.", + "type": "integer" + }, + "destination": { + "description": "The destination port on the container.", + "type": "integer" + } + }, + "required": ["source", "destination"], + "additionalProperties": false + } + } + }, + "required": ["image"], + "additionalProperties": false, + "examples": [ + { + "image": { + "repository": "vyos/vyos-build", + "tag": "crux" + }, + "env": { + "TZ": "UTC" + } + } + ] + }, + { + "title": "Networking fields", + "oneOf": [ + { + "title": "Host networking", + "properties": { + "allow-host-networks": { + "description": "Run the container with host networking enabled.", + "type": "boolean" + } + }, + "required": ["allow-host-networks"], + "additionalProperties": false, + "examples": [ + { + "allow-host-networks": true + } + ] + }, + { + "title": "Container networking", + "description": "Use this if you want to configure this container with container networking.", + "properties": { + "networks": { + "description": "Configures the container networks the container should run on.", + "type": "object", + "additionalProperties": { + "type": "object", + "title": "Container network entry", + "properties": { + "ipv4_hostid": { + "description": "The host id that is used to generate the IP address on the specified network", + "type": "integer" + } + }, + "additionalProperties": false + } + } + }, + "required": ["networks"], + "additionalProperties": false, + "examples": [ + { + "networks": { + "services": { + "ipv4_hostid": 10 + } + } + } + ] + } + ] + } + ] + } + } +} diff --git a/defaults/schemas/vyos_coredns.schema.json b/defaults/schemas/vyos_coredns.schema.json new file mode 100644 index 0000000..47394ad --- /dev/null +++ b/defaults/schemas/vyos_coredns.schema.json @@ -0,0 +1,114 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "vyos_coredns.schema.json", + "title": "vyos_coredns", + "description": "VyOS CoreDNS configuration", + "type": "object", + "properties": { + "enabled": { + "description": "Enable a CoreDNS container.", + "type": "boolean", + "examples": [{"enabled": true}] + }, + "config_path": { + "description": "Path on the VyOS router where the configuration is stored.", + "type": "string", + "examples": [{"config_path": "/config/coredns"}] + }, + "container": { + "description": "CoreDNS container configuration", + "type": "object", + "properties": { + "repository": { + "description": "Configure the CoreDNS image repository.", + "type": "string" + }, + "tag": { + "description": "Configure the CoreDNS image tag.", + "type": "string" + }, + "name": { + "description": "Configure the CoreDNS container name. Defaults to `vyos-coredns`", + "type": "string" + }, + "networks": { + "description": "Configure CoreDNS container networking. If not present, the container will be configured with host networking.", + "type": "object", + "additionalProperties": { + "type": "object", + "title": "Container network entry", + "properties": { + "ipv4_hostid": { + "description": "The host id that is used to generate the IP address on the specified network", + "type": "integer" + } + }, + "additionalProperties": false, + "examples": [ + { + "networks": { + "services": { + "ipv4_hostid": 2 + } + } + } + ] + } + } + }, + "additionalProperties": false, + "required": ["repository", "tag"], + "examples": [ + { + "container": { + "repository": "ghcr.io/k8s-at-home/coredns", + "tag": "v1.8.4" + } + } + ] + }, + "k8s_gateway": { + "description": "CoreDNS k8s_gateway plugin configuration", + "type": "object", + "properties": { + "enabled": { + "description": "Enable the k8s_gateway plugin.", + "type": "boolean" + }, + "api_ip": { + "description": "Address where the K8s API can be reached.", + "type": "string" + }, + "domains": { + "description": "Configure the domains which should be handled by the k8s_gatway plugin.", + "type": "array", + "items": { + "type": "string" + } + }, + "service_account_ns": { + "description": "Namespace where the ServiceAccount is defined.", + "type": "string" + }, + "service_account": { + "description": "Name of the ServiceAccount that is used to communicate with the K8s API. This role does *not* create the ServiceAccount.", + "type": "string" + } + }, + "additionalProperties": false, + "required": ["api_ip", "domains", "service_account_ns", "service_account"], + "examples": [ + { + "k8s_gateway": { + "enabled": true, + "api_ip": "10.0.0.10", + "domains": ["example.com"], + "service_account_ns": "default", + "service_account": "coredns" + } + } + ] + } + }, + "additionalProperties": false +} diff --git a/docs/_assets/css/fancybox.css b/docs/_assets/css/fancybox.css deleted file mode 100644 index 7c4823c..0000000 --- a/docs/_assets/css/fancybox.css +++ /dev/null @@ -1 +0,0 @@ -.not-selectable{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.carousel{position:relative;box-sizing:border-box}.carousel *,.carousel *:before,.carousel *:after{box-sizing:inherit}.carousel__viewport{position:relative;overflow:hidden;width:100%;height:100%}.carousel__track{display:flex}.carousel__slide{flex:0 0 auto;width:var(--carousel-slide-width, 60%);max-width:100%;padding:1rem;position:relative;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;touch-action:pan-y}.carousel.has-dots{margin-bottom:calc(0.5rem + 22px)}.carousel__dots{margin:0 auto;padding:0;position:absolute;top:calc(100% + 0.5rem);left:0;right:0;display:flex;justify-content:center;list-style:none;user-select:none}.carousel__dots .carousel__dot{margin:0;padding:0;display:block;position:relative;width:22px;height:22px;cursor:pointer}.carousel__dots .carousel__dot:after{content:"";width:8px;height:8px;border-radius:50%;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);background-color:currentColor;opacity:.25;transition:opacity .15s ease-in-out}.carousel__dots .carousel__dot.is-selected:after{opacity:1}.carousel__button{width:var(--carousel-button-width, 48px);height:var(--carousel-button-height, 48px);padding:0;border:0;display:flex;justify-content:center;align-items:center;pointer-events:all;cursor:pointer;color:var(--carousel-button-color, currentColor);background:var(--carousel-button-bg, transparent);border-radius:var(--carousel-button-border-radius, 50%);box-shadow:var(--carousel-button-shadow, none)}.carousel__button.is-prev,.carousel__button.is-next{position:absolute;top:50%;transform:translateY(-50%)}.carousel__button.is-prev{left:10px}.carousel__button.is-next{right:10px}.carousel__button[disabled]{cursor:default;opacity:.3}.carousel__button svg{width:var(--carousel-button-svg-width, 24px);height:var(--carousel-button-svg-height, 24px);fill:none;stroke:currentColor;stroke-width:var(--carousel-button-svg-stroke-width, 1.5);stroke-linejoin:bevel;stroke-linecap:round;filter:var(--carousel-button-svg-filter, none);pointer-events:none}body.compensate-for-scrollbar{overflow:hidden !important;touch-action:none}.fancybox__container{position:fixed;top:0;left:0;bottom:0;right:0;margin:0;padding:env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);box-sizing:border-box;display:flex;flex-direction:column;color:var(--fancybox-color, #fff);-webkit-tap-highlight-color:transparent;overflow:hidden;z-index:1050;outline:none;transform-origin:top left;--carousel-button-width: 48px;--carousel-button-height: 48px;--carousel-button-svg-width: 24px;--carousel-button-svg-height: 24px;--carousel-button-svg-stroke-width: 2.5;--carousel-button-svg-filter: drop-shadow(1px 1px 1px rgba(0, 0, 0, 0.4))}.fancybox__container *,.fancybox__container *::before,.fancybox__container *::after{box-sizing:inherit}.fancybox__container :focus{outline:thin dotted}body.is-using-mouse .fancybox__container :focus{outline:none}@media all and (min-width: 1024px){.fancybox__container{--carousel-button-svg-width:27px;--carousel-button-svg-height:27px}}.fancybox__backdrop{position:absolute;top:0;right:0;bottom:0;left:0;z-index:-1;background:var(--fancybox-bg, rgba(24, 24, 27, 0.92))}.fancybox__carousel{position:relative;flex:1 1 auto;min-height:0;height:100%;z-index:10}.fancybox__carousel.has-dots{margin-bottom:calc(0.5rem + 22px)}.fancybox__viewport{position:relative;width:100%;height:100%;overflow:visible;cursor:default}.fancybox__track{display:flex;height:100%}.fancybox__slide{flex:0 0 auto;width:100%;max-width:100%;margin:0;padding:64px;position:relative;overscroll-behavior:contain;display:flex;flex-direction:column;outline:0;overflow:auto;-webkit-overflow-scrolling:touch;--carousel-button-width: 36px;--carousel-button-height: 36px;--carousel-button-svg-width: 22px;--carousel-button-svg-height: 22px}.fancybox__slide::before,.fancybox__slide::after{content:"";flex:0 0 0;margin:auto}@media all and (min-width: 1024px){.fancybox__slide{padding:64px 100px}}.fancybox__content{margin:0 env(safe-area-inset-right, 0px) 0 env(safe-area-inset-left, 0px);padding:36px;color:var(--fancybox-content-color, #374151);background:var(--fancybox-content-bg, #fff);position:relative;align-self:center;display:flex;flex-direction:column;z-index:20}.fancybox__caption{align-self:center;max-width:100%;margin:0;padding:1rem 0 0 0;line-height:1.375;color:var(--fancybox-color, currentColor);visibility:visible;cursor:auto;flex-shrink:0}.is-loading .fancybox__caption{visibility:hidden}.fancybox__container>.carousel__dots{top:100%;color:var(--fancybox-color, #fff)}.fancybox__nav .carousel__button{z-index:40}.fancybox__nav .carousel__button.is-next{right:8px}@media all and (min-width: 1024px){.fancybox__nav .carousel__button.is-next{right:40px}}.fancybox__nav .carousel__button.is-prev{left:8px}@media all and (min-width: 1024px){.fancybox__nav .carousel__button.is-prev{left:40px}}.carousel__button.is-close{position:absolute;top:8px;right:8px;top:calc(env(safe-area-inset-top, 0px) + 8px);right:calc(env(safe-area-inset-right, 0px) + 8px);z-index:40}@media all and (min-width: 1024px){.carousel__button.is-close{right:40px}}.fancybox__content>.carousel__button.is-close{position:absolute;top:-36px;right:0;color:var(--fancybox-color, #fff)}.fancybox__spinner{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:50px;height:50px;cursor:pointer;z-index:1053;color:var(--fancybox-color, currentColor)}.fancybox__spinner svg{animation:fancybox-rotate 2s linear infinite;transform-origin:center center;position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:100%;height:100%}.fancybox__spinner svg circle{fill:none;stroke-width:2.5;stroke-miterlimit:10;stroke-dasharray:1,200;stroke-dashoffset:0;animation:fancybox-dash 1.5s ease-in-out infinite;stroke-linecap:round;stroke:currentColor}@keyframes fancybox-rotate{100%{transform:rotate(360deg)}}@keyframes fancybox-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}100%{stroke-dasharray:89,200;stroke-dashoffset:-124px}}.fancybox__container.is-animated[aria-hidden=false] .fancybox__backdrop,.fancybox__container.is-animated[aria-hidden=false] .fancybox__caption,.fancybox__container.is-animated[aria-hidden=false] .fancybox__nav,.fancybox__container.is-animated[aria-hidden=false] .carousel__dots,.fancybox__container.is-animated[aria-hidden=false] .carousel__button.is-close{transition:opacity var(--fancybox-ts, 0.25s) ease;opacity:var(--fancybox-opacity, 1)}.fancybox__container.is-animated[aria-hidden=true] .fancybox__backdrop,.fancybox__container.is-animated[aria-hidden=true] .fancybox__caption,.fancybox__container.is-animated[aria-hidden=true] .fancybox__nav,.fancybox__container.is-animated[aria-hidden=true] .carousel__dots,.fancybox__container.is-animated[aria-hidden=true] .carousel__button.is-close{transition:opacity .2s ease;opacity:0}.fancybox-fadeIn{animation:.2s ease both fancybox-fadeIn}.fancybox-fadeOut{animation:.2s ease both fancybox-fadeOut}.fancybox-zoomInUp{animation:.2s ease both fancybox-zoomInUp}.fancybox-zoomOutDown{animation:.2s ease both fancybox-zoomOutDown}.fancybox-throwOutUp{animation:.2s ease both fancybox-throwOutUp}.fancybox-throwOutDown{animation:.2s ease both fancybox-throwOutDown}@keyframes fancybox-fadeIn{from{opacity:0}to{opacity:1}}@keyframes fancybox-fadeOut{to{opacity:0}}@keyframes fancybox-zoomInUp{from{transform:scale(0.97) translate3d(0, 16px, 0);opacity:0}to{transform:scale(1) translate3d(0, 0, 0);opacity:1}}@keyframes fancybox-zoomOutDown{to{transform:scale(0.97) translate3d(0, 16px, 0);opacity:0}}@keyframes fancybox-throwOutUp{20%{opacity:.5}to{transform:translate3d(0, -30%, 0);opacity:0}}@keyframes fancybox-throwOutDown{20%{opacity:.5}to{transform:translate3d(0, 30%, 0);opacity:0}}.fancybox__carousel .carousel__slide{scrollbar-width:thin;scrollbar-color:#ccc rgba(255,255,255,.1)}.fancybox__carousel .carousel__slide::-webkit-scrollbar{width:8px;height:8px}.fancybox__carousel .carousel__slide::-webkit-scrollbar-track{background-color:rgba(255,255,255,.1)}.fancybox__carousel .carousel__slide::-webkit-scrollbar-thumb{background-color:#ccc;border-radius:2px;box-shadow:inset 0 0 4px rgba(0,0,0,.2)}.fancybox__carousel .is-draggable{cursor:move;cursor:grab}.fancybox__carousel .is-dragging{cursor:move;cursor:grabbing}.fancybox__carousel .carousel__slide:not(.has-image) .fancybox__content{cursor:auto}.fancybox__carousel .carousel__slide.can-zoom_in .fancybox__content{cursor:zoom-in}.fancybox__carousel .carousel__slide.can-zoom_out .fancybox__content{cursor:zoom-out}.fancybox__image{background:transparent;user-select:none}.has-image .fancybox__content{padding:0;background:transparent}.is-closing .has-image .fancybox__content{overflow:visible}.has-image[data-image-fit=contain]{overflow:visible;touch-action:none}.has-image[data-image-fit=contain] .fancybox__content{min-height:1px}.has-image[data-image-fit=contain] .fancybox__image{max-width:100%;min-height:1px;object-fit:contain;background:transparent}.has-image[data-image-fit=contain-w]{overflow-x:hidden;overflow-y:auto}.has-image[data-image-fit=contain-w] .fancybox__content{min-height:auto}.has-image[data-image-fit=contain-w] .fancybox__image{max-width:100%;height:auto}.has-image[data-image-fit=cover]{overflow:visible;touch-action:none}.has-image[data-image-fit=cover] .fancybox__content{min-height:1px;width:100%;height:100%}.has-image[data-image-fit=cover] .fancybox__image{width:100%;height:100%;min-height:1px;object-fit:cover}.fancybox__carousel .fancybox__slide.has-iframe .fancybox__content,.fancybox__carousel .fancybox__slide.has-map .fancybox__content,.fancybox__carousel .fancybox__slide.has-pdf .fancybox__content,.fancybox__carousel .fancybox__slide.has-video .fancybox__content,.fancybox__carousel .fancybox__slide.has-html5video .fancybox__content{flex-shrink:1;min-height:1px;overflow:visible}.fancybox__carousel .fancybox__slide.has-iframe .fancybox__content,.fancybox__carousel .fancybox__slide.has-map .fancybox__content,.fancybox__carousel .fancybox__slide.has-pdf .fancybox__content{width:100%;height:80%}.fancybox__carousel .fancybox__slide.has-video .fancybox__content,.fancybox__carousel .fancybox__slide.has-html5video .fancybox__content{width:960px;height:540px;max-width:100%;max-height:100%}.fancybox__carousel .fancybox__slide.has-map .fancybox__content,.fancybox__carousel .fancybox__slide.has-pdf .fancybox__content,.fancybox__carousel .fancybox__slide.has-video .fancybox__content,.fancybox__carousel .fancybox__slide.has-html5video .fancybox__content{padding:0;background:rgba(24,24,27,.9);color:#fff}.fancybox__carousel .fancybox__slide.has-map .fancybox__content{background:#e5e3df}.fancybox__html5video,.fancybox__iframe{border:0;display:block;height:100%;width:100%;background:transparent}.fancybox-placeholder{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0}.fancybox__thumbs{flex:0 0 auto;position:relative;padding:0px 3px}.fancybox__container.is-animated[aria-hidden=true] .fancybox__thumbs{transition:unset;opacity:0}.fancybox__container.is-animated[aria-hidden=false] .fancybox__thumbs{transition:opacity var(--fancybox-ts, 0.25s) ease-in;opacity:var(--fancybox-opacity, 1)}.fancybox__thumbs .carousel__slide{flex:0 0 auto;width:var(--fancybox-thumbs-width, 96px);margin:0;padding:8px 3px;box-sizing:content-box;display:flex;align-items:center;justify-content:center;overflow:visible;cursor:pointer}.fancybox__thumbs .carousel__slide.is-nav-selected::after{content:"";position:absolute;top:0;left:3px;right:3px;bottom:3px;border-bottom:3px solid currentColor}.fancybox__thumbs .carousel__slide>*{pointer-events:none;user-select:none}.fancybox__thumb{position:relative;width:100%;padding-top:calc(100% / (var(--fancybox-thumbs-ratio, 1.5)));background-size:cover;background-position:center center;background-color:rgba(255,255,255,.1);background-repeat:no-repeat} diff --git a/docs/_assets/js/fancybox.umd.js b/docs/_assets/js/fancybox.umd.js deleted file mode 100644 index aa50bcf..0000000 --- a/docs/_assets/js/fancybox.umd.js +++ /dev/null @@ -1,2 +0,0 @@ -// @fancyapps/ui/Fancybox v4.0.0-alpha.4 -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).window=t.window||{})}(this,(function(t){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function n(t,e){for(var i=0;it.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,a=!0,r=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return a=t.done,t},e:function(t){r=!0,s=t},f:function(){try{a||null==i.return||i.return()}finally{if(r)throw s}}}}var g=function(t){return"object"===e(t)&&null!==t&&t.constructor===Object&&"[object Object]"===Object.prototype.toString.call(t)},m=function t(){for(var i=!1,n=arguments.length,o=new Array(n),s=0;s1&&void 0!==arguments[1]?arguments[1]:1e3;return t=parseFloat(t)||0,Math.round((t+Number.EPSILON)*e)/e},b="undefined"!=typeof window&&window.ResizeObserver||function(){function t(e){i(this,t),this.observables=[],this.boundCheck=this.check.bind(this),this.boundCheck(),this.callback=e}return o(t,[{key:"observe",value:function(t){if(!this.observables.some((function(e){return e.el===t}))){var e={el:t,size:{height:t.clientHeight,width:t.clientWidth}};this.observables.push(e)}}},{key:"unobserve",value:function(t){this.observables=this.observables.filter((function(e){return e.el!==t}))}},{key:"disconnect",value:function(){this.observables=[]}},{key:"check",value:function(){var t=this.observables.filter((function(t){var e=t.el.clientHeight,i=t.el.clientWidth;if(t.size.height!==e||t.size.width!==i)return t.size.height=e,t.size.width=i,!0})).map((function(t){return t.el}));t.length>0&&this.callback(t),window.requestAnimationFrame(this.boundCheck)}}]),t}(),w=function t(e){return!(!e||e.classList.contains("carousel__track")||e===document.body)&&(function(t){var e=window.getComputedStyle(t)["overflow-y"],i=window.getComputedStyle(t)["overflow-x"],n=("scroll"===e||"auto"===e)&&Math.abs(t.scrollHeight-t.clientHeight)>1,o=("scroll"===i||"auto"===i)&&Math.abs(t.scrollWidth-t.clientWidth)>1;return n||o}(e)?e:t(e.parentNode))},x=function(t){var e=0;return t&&(e=t instanceof SVGElement?Math.min(t.getClientRects()[0].height,t.height.baseVal.value):Math.max(t.offsetHeight,t.scrollHeight)),e},k=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};i(this,t),this.options=m(!0,{},e),this.plugins=[],this.events={};for(var n=0,o=["on","once"];n1&&void 0!==arguments[1]?arguments[1]:[];return String(t).replace(/\{\{(\w+).?(\w+)?\}\}/g,(function(t,n,o){var s=!1;if(!(s=o?e.option("".concat(n[0]+n.toLowerCase().substring(1),".l10n.").concat(o)):e.option("l10n.".concat(n))))return n;for(var a=0;a1?e-1:0),n=1;n1&&void 0!==arguments[1]?arguments[1]:{};if(i(this,n),s=m(!0,{},$,s),o=e.call(this,s),!(t instanceof HTMLElement))throw new Error("Viewport not found");o.state="init",o.$viewport=t;for(var a=0,r=["onPointerDown","onPointerMove","onPointerUp","onWheel","onClick"];a0&&t>0?e/t*this.maxScale:this.maxScale},t.updateMetrics(),t.trigger(e>0?"load":"error")};!0!==this.$content.complete?(this.$content.onload=function(){return e()},this.$content.onerror=function(){return e()}):e()}else this.updateMetrics()}},{key:"resetValues",value:function(){this.viewportDim={top:0,left:0,width:0,height:0},this.contentDim={width:0,height:0},this.friction=this.option("friction"),this.current={x:0,y:0,scale:1},this.velocity={x:0,y:0,scale:0},this.pan={x:0,y:0,scale:1},this.drag={startTime:null,firstPosition:null,startPosition:null,startPoint:null,startDistance:null,endPosition:null,endPoint:null,distance:0,distanceX:0,distanceY:0,elapsedTime:0},this.lockAxis=null,this.pendingAnimateUpdate=null,this.pendingResizeUpdate=null,this.pointers=[]}},{key:"updateMetrics",value:function(){var t,e,i=this.$viewport.getBoundingClientRect(),n=i.top,o=i.left,s=i.width,a=i.height,r=window.getComputedStyle(this.$viewport);s-=parseFloat(r.paddingLeft)+parseFloat(r.paddingRight),a-=parseFloat(r.paddingTop)+parseFloat(r.paddingBottom),this.viewportDim={top:n,left:o,width:s,height:a},this.contentDim={width:this.option("width",(t=this.$content,e=0,t&&(e=t instanceof SVGElement?Math.min(t.getClientRects()[0].width,t.width.baseVal.value):Math.max(t.offsetWidth,t.scrollWidth)),e)),height:this.option("hidth",x(this.$content))},this.trigger("updateMetrics"),this.updateBounds()}},{key:"updateBounds",value:function(t){var e={from:0,to:0},i={from:0,to:0};if(t||(t=this.velocity.scale?this.pan.scale:this.current.scale),t<1)return[e,i];var n=this.contentDim,o=this.viewportDim,s=n.width*t,a=n.height*t;return e.to=y(.5*(s-n.width)),n.width>o.width?e.from=y(e.to+o.width-s):e.from=y(-1*e.to),i.to=y(.5*(a-n.height)),n.height>o.height?i.from=y(i.to+o.height-a):i.from=y(-1*i.to),this.boundX=e,this.boundY=i,this.trigger("updateBounds",t),[this.boundX,this.boundY]}},{key:"zoomIn",value:function(t){this.zoomTo(this.current.scale+(t||this.option("step")))}},{key:"zoomOut",value:function(t){this.zoomTo(this.current.scale-(t||this.option("step")))}},{key:"toggleZoom",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=this.option("maxScale"),i=this.option("baseScale");this.zoomTo(this.current.scale>i+.5*(e-i)?i:e,t)}},{key:"zoomTo",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=e.x,n=void 0===i?null:i,o=e.y,s=void 0===o?null:o,a=e.friction,r=void 0===a?this.option("zoomFriction"):a;t||(t=this.option("baseScale")),t=Math.max(Math.min(t,this.option("maxScale")),this.option("minScale"));var l=this.contentDim.width,c=this.contentDim.height,h=l*this.current.scale,d=c*this.current.scale,u=l*t,f=c*t;null===n&&(n=.5*h),null===s&&(s=.5*d),!1===this.option("zoomInCentered")&&(n<.5*h&&(n=h),n>h&&(n=0),s<0&&(s=d),s>d&&(s=0));var v=h>0?n/h:0,p=d>0?s/d:0,g=(u-h)*(v-.5),m=(f-d)*(p-.5);Math.abs(g)<1&&(g=0),Math.abs(m)<1&&(m=0),n=this.current.x-g,s=this.current.y-m,this.panTo({x:n,y:s,scale:t,friction:r})}},{key:"panTo",value:function(t){var e=t.x,i=void 0===e?0:e,n=t.y,o=void 0===n?0:n,s=t.scale,a=void 0===s?this.current.scale:s,r=t.friction,l=void 0===r?this.option("friction"):r,c=t.ignoreBounds,h=void 0!==c&&c;if(l||this.stopMoving(),!0!==h){var u=d(this.updateBounds(a),2),f=u[0],v=u[1];f&&(i=Math.max(Math.min(i,f.to),f.from)),v&&(o=Math.max(Math.min(o,v.to),v.from))}return l>0&&(Math.abs(i-this.current.x)>.1||Math.abs(o-this.current.y)>.1||Math.abs(a-this.current.scale)>.1)?(this.state="panning",this.friction=l,this.pan={x:i,y:o,scale:a},this.velocity={x:(1/this.friction-1)*(i-this.current.x),y:(1/this.friction-1)*(o-this.current.y),scale:(1/this.friction-1)*(a-this.current.scale)},this.animate(),this):(this.pendingAnimateUpdate&&(cancelAnimationFrame(this.pendingAnimateUpdate),this.pendingAnimateUpdate=null),this.state="ready",this.stopMoving(),this.current={x:i,y:o,scale:a},this.transform(),this.handleCursor(),this.trigger("afterAnimate",!0),this)}},{key:"animate",value:function(){var t=this;if(!this.pendingAnimateUpdate){if(this.applyBoundForce(),this.applyDragForce(),this.velocity.x*=this.friction,this.velocity.y*=this.friction,this.velocity.scale*=this.friction,this.current.x+=this.velocity.x,this.current.y+=this.velocity.y,this.current.scale+=this.velocity.scale,"dragging"==this.state||"pointerdown"==this.state||Math.abs(this.velocity.x)>.05||Math.abs(this.velocity.y)>.05||Math.abs(this.velocity.scale)>.05)return this.transform(),void(this.pendingAnimateUpdate=requestAnimationFrame((function(){t.pendingAnimateUpdate=null,t.animate()})));this.current.x=y(this.current.x+this.velocity.x/(1/this.friction-1)),this.current.y=y(this.current.y+this.velocity.y/(1/this.friction-1)),Math.abs(this.current.x)<.5&&(this.current.x=0),Math.abs(this.current.y)<.5&&(this.current.y=0),this.current.scale=y(this.current.scale+this.velocity.scale/(1/this.friction-1),1e4),Math.abs(this.current.scale-1)<.01&&(this.current.scale=1),this.state="ready",this.stopMoving(),this.transform(),this.handleCursor(),this.trigger("afterAnimate")}}},{key:"handleCursor",value:function(){var t=this.option("draggableClass");t&&this.option("touch")&&(this.contentDim.width<=this.viewportDim.width&&1==this.option("panOnlyZoomed")&&this.current.scale<=this.option("baseScale")?this.$viewport.classList.remove(t):this.$viewport.classList.add(t))}},{key:"isMoved",value:function(){return 0!==this.current.x||0!==this.current.y||1!==this.current.scale||this.velocity.x>0||this.velocity.y>0||this.velocity.scale>0}},{key:"stopMoving",value:function(){this.velocity={x:0,y:0,scale:0}}},{key:"transform",value:function(){this.trigger("beforeTransform");var t=y(this.current.x,100),e=y(this.current.y,100),i=y(this.current.scale,1e4);Math.abs(t)<=.1&&Math.abs(e)<=.1&&Math.abs(i-1)<=.1?this.$content.style.transform="":this.$content.style.transform="translate(".concat(t,"px, ").concat(e,"px) scale(").concat(i,")"),this.trigger("afterTransform")}},{key:"applyBoundForce",value:function(){if("decel"===this.state){var t,e,i,n,o={x:0,y:0},s=this.option("bounceForce"),a=this.boundX,r=this.boundY;if(a&&(t=this.current.xa.to),r&&(i=this.current.yr.to),t||e){var l=(t?a.from:a.to)-this.current.x,c=l*s,h=this.current.x+(this.velocity.x+c)/(1/this.friction-1);t&&ha.to||(c=l*s-this.velocity.x),o.x=c}if(i||n){var d=(i?r.from:r.to)-this.current.y,u=d*s,f=this.current.y+(this.velocity.y+u)/(1/this.friction-1);i&&fr.to||(u=d*s-this.velocity.y),o.y=u}this.velocity.x+=o.x,this.velocity.y+=o.y}}},{key:"applyDragForce",value:function(){"dragging"===this.state&&(this.velocity={x:(1/this.friction-1)*(this.drag.endPosition.x-this.current.x),y:(1/this.friction-1)*(this.drag.endPosition.y-this.current.y),scale:(1/this.friction-1)*(this.drag.endPosition.scale-this.current.scale)})}},{key:"attachEvents",value:function(){var t=this,e=this.$viewport;this.resizeObserver=this.resizeObserver||new b((function(e){t.pendingResizeUpdate=t.pendingResizeUpdate||setTimeout((function(){var i=e&&e[0].contentRect;!i&&t.$viewport&&(i=t.$viewport.getBoundingClientRect()),i&&(Math.abs(i.width-t.viewportDim.width)>1||Math.abs(i.height-t.viewportDim.height)>1)&&t.updateMetrics(),t.pendingResizeUpdate=null}),t.option("updateRate",250))})),this.resizeObserver.observe(e),e.addEventListener("click",this.onClick,{passive:!1}),e.addEventListener("wheel",this.onWheel,{passive:!1}),this.option("touch")&&(window.PointerEvent?(e.addEventListener("pointerdown",this.onPointerDown,{passive:!1}),e.addEventListener("pointermove",this.onPointerMove,{passive:!1}),e.addEventListener("pointerup",this.onPointerUp),e.addEventListener("pointercancel",this.onPointerUp)):(e.addEventListener("touchstart",this.onPointerDown,{passive:!1}),e.addEventListener("touchmove",this.onPointerMove,{passive:!1}),e.addEventListener("touchend",this.onPointerUp),e.addEventListener("touchcancel",this.onPointerUp),e.addEventListener("mousedown",this.onPointerDown)))}},{key:"detachEvents",value:function(){this.resizeObserver&&this.resizeObserver.disconnect(),this.resizeObserver=null,this.pendingResizeUpdate&&(clearTimeout(this.pendingResizeUpdate),this.pendingResizeUpdate=null);var t=this.$viewport;window.PointerEvent?(t.removeEventListener("pointerdown",this.onPointerDown,{passive:!1}),t.removeEventListener("pointermove",this.onPointerMove,{passive:!1}),t.removeEventListener("pointerup",this.onPointerUp),t.removeEventListener("pointercancel",this.onPointerUp)):(t.removeEventListener("touchstart",this.onPointerDown,{passive:!1}),t.removeEventListener("touchmove",this.onPointerMove,{passive:!1}),t.removeEventListener("touchend",this.onPointerUp),t.removeEventListener("touchcancel",this.onPointerUp),t.removeEventListener("mousedown",this.onPointerDown)),t.removeEventListener("click",this.onClick,{passive:!1}),t.removeEventListener("wheel",this.onWheel,{passive:!1})}},{key:"copyPointer",value:function(t){return{pointerId:t.pointerId,clientX:t.clientX,clientY:t.clientY}}},{key:"findPointerIndex",value:function(t){for(var e=this.pointers.length;e--;)if(this.pointers[e].pointerId===t.pointerId)return e;return-1}},{key:"addPointer",value:function(t){var e=0;if(t.touches&&t.touches.length){var i,n=p(t.touches);try{for(n.s();!(i=n.n()).done;){var o=i.value;o.pointerId=e++,this.addPointer(o)}}catch(t){n.e(t)}finally{n.f()}}else(e=this.findPointerIndex(t))>-1&&this.pointers.splice(e,1),this.pointers.push(t)}},{key:"removePointer",value:function(t){if(t.touches)for(;this.pointers.length;)this.pointers.pop();else{var e=this.findPointerIndex(t);e>-1&&this.pointers.splice(e,1)}}},{key:"getMiddlePoint",value:function(){var t=u(this.pointers),e=(t=t.sort((function(t,e){return e.pointerId-t.pointerId}))).shift(),i=t.shift();return i?{clientX:.5*(e.clientX-i.clientX)+i.clientX,clientY:.5*(e.clientY-i.clientY)+i.clientY}:{clientX:e?e.clientX:0,clientY:e?e.clientY:0}}},{key:"getDistance",value:function(t,e){if(!(t=(t=t||u(this.pointers)).slice())||t.length<2)return 0;var i=(t=t.sort((function(t,e){return e.pointerId-t.pointerId}))).shift(),n=t.shift(),o=Math.abs(n.clientX-i.clientX);if("x"===e)return o;var s=Math.abs(n.clientY-i.clientY);return"y"===e?s:Math.sqrt(Math.pow(o,2)+Math.pow(s,2))}},{key:"resetDragState",value:function(){var t=this.$content.getClientRects()[0],e=t.left,i=t.top,n=this.getMiddlePoint(),o={top:i,left:e,x:this.current.x,y:this.current.y,scale:this.current.scale};m(this.drag,{startPosition:m({},o),startPoint:m({},n),startDistance:this.getDistance(),endPosition:m({},o),endPoint:m({},n),distance:0,distanceX:0,distanceY:0}),"pointerdown"===this.state&&(this.lockAxis=null,this.drag.startTime=new Date,this.drag.firstPosition=Object.assign({},o)),this.stopMoving(),this.friction=this.option("friction")}},{key:"onPointerDown",value:function(t){if(t&&!(t.button&&t.button>0))if(this.option("panOnlyZoomed")&&this.velocity.scale)t.preventDefault();else{if(this.resetDragState(),!this.pointers.length){if(-1!==["BUTTON","TEXTAREA","OPTION","INPUT","SELECT","VIDEO"].indexOf(t.target.nodeName))return;if(this.option("textSelection")&&function(t,e,i){for(var n=t.childNodes,o=document.createRange(),s=0;s=r.left&&i>=r.top&&e<=r.right&&i<=r.bottom)return a}}return!1}(t.target,t.clientX,t.clientY))return;if(w(t.target))return}var e;if((e=window.getSelection?window.getSelection():document.selection)&&e.rangeCount&&e.getRangeAt(0).getClientRects().length&&(e.removeAllRanges?e.removeAllRanges():e.empty&&e.empty()),this.pointers.length>1||this.pointers.length&&this.lockAxis)t.preventDefault();else if(!1!==this.trigger("touchStart",t))if(t.preventDefault(),this.state="pointerdown",this.addPointer(this.copyPointer(t)),this.resetDragState(),window.PointerEvent)try{t.target.setPointerCapture(t.pointerId)}catch(t){}else document.addEventListener("mousemove",this.onPointerMove,{passive:!1}),document.addEventListener("mouseup",this.onPointerUp,{passive:!1})}}},{key:"onPointerMove",value:function(t){if(!(t.targetTouches&&t.targetTouches.length>1||"pointerdown"!==this.state&&"dragging"!==this.state))if(0!=this.trigger("touchMove",t)){if(this.addPointer(this.copyPointer(t)),!(this.pointers.length>1&&!1===this.option("pinchToZoom")))if(1==this.option("panOnlyZoomed")&&this.current.scale===this.option("baseScale")&&this.pointers.length<2)t.preventDefault();else{var e=this.getMiddlePoint(),i=[e,this.drag.startPoint];this.drag.distance=this.getDistance(i);var n=this.events.click&&this.events.click.length||this.events.doubleClick&&this.events.doubleClick.length||this.option.click||this.option.doubleClick;if(!(this.drag.distance<6&&(n||this.option("lockAxis")&&!this.lockAxis))&&("pointerdown"==this.state&&(this.state="dragging"),"dragging"===this.state)){var o=this.option("lockAxis");if(!this.lockAxis&&o)if("xy"===o){var s=this.getDistance(i,"x"),a=this.getDistance(i,"y"),r=Math.abs(180*Math.atan2(a,s)/Math.PI);this.lockAxis=r>45&&r<135?"y":"x"}else this.lockAxis=o;t.preventDefault(),t.stopPropagation(),this.$viewport.classList.add(this.option("draggingClass")),this.animate();var l=this.current.scale,c=0,h=0;if(this.current.scale===this.option("baseScale")&&"y"===this.lockAxis||(c=e.clientX-this.drag.startPoint.clientX),this.current.scale===this.option("baseScale")&&"x"===this.lockAxis||(h=e.clientY-this.drag.startPoint.clientY),this.drag.endPosition.x=this.drag.startPosition.x+c,this.drag.endPosition.y=this.drag.startPosition.y+h,this.pointers.length>1){this.drag.middlePoint=e,l=this.drag.startPosition.scale*this.getDistance()/this.drag.startDistance,l=Math.max(Math.min(l,2*this.option("maxScale")),.5*this.option("minScale"));var d=this.$content.width,u=this.$content.height,f=d*this.drag.startPosition.scale,v=u*this.drag.startPosition.scale,p=u*l,g=(d*l-f)*((this.drag.startPoint.clientX-this.drag.startPosition.left)/f-.5),m=(p-v)*((this.drag.startPoint.clientY-this.drag.startPosition.top)/v-.5);this.drag.endPosition.x-=g,this.drag.endPosition.y-=m,this.drag.endPosition.scale=l,this.updateBounds(l)}this.applyDragResistance()}}}else t.preventDefault()}},{key:"onPointerUp",value:function(t){if(this.removePointer(t),window.PointerEvent)try{t.target.releasePointerCapture(t.pointerId)}catch(t){}else document.removeEventListener("mousemove",this.onPointerMove,{passive:!1}),document.removeEventListener("mouseup",this.onPointerUp,{passive:!1});if(this.pointers.length>0)return t.preventDefault(),void this.resetDragState();if("pointerdown"===this.state||"dragging"===this.state){this.$viewport.classList.remove(this.option("draggingClass"));var e=this.$content.getClientRects()[0],i=e.top,n=e.left,o=this.drag;if(m(!0,o,{elapsedTime:new Date-o.startTime,distanceX:o.endPosition.x-o.firstPosition.x,distanceY:o.endPosition.y-o.firstPosition.y,endPosition:{top:i,left:n}}),o.distance=Math.sqrt(Math.pow(o.distanceX,2)+Math.pow(o.distanceY,2)),this.state="decel",this.friction=this.option("decelFriction"),this.pan={x:this.current.x+this.velocity.x/(1/this.friction-1),y:this.current.y+this.velocity.y/(1/this.friction-1),scale:this.current.scale+this.velocity.scale/(1/this.friction-1)},!1!==this.trigger("touchEnd",t)&&"decel"===this.state){var s=this.option("minScale");if(this.current.scale.01){var r={friction:.64};o.middlePoint&&(r.x=o.middlePoint.clientX-n,r.y=o.middlePoint.clientY-i),this.zoomTo(a,r)}}}}}},{key:"applyDragResistance",value:function(){var t,e,i,n,o=this.boundX,s=this.boundY;if(o&&(t=this.drag.endPosition.xo.to),s&&(i=this.drag.endPosition.ys.to),t||e){var a=t?o.from:o.to,r=this.drag.endPosition.x-a;this.drag.endPosition.x=a+.3*r}if(i||n){var l=i?s.from:s.to,c=this.drag.endPosition.y-l;this.drag.endPosition.y=l+.3*c}}},{key:"onWheel",value:function(t){!1!==this.trigger("wheel",t)&&"zoom"==this.option("wheel",t)&&this.zoomWithWheel(t)}},{key:"zoomWithWheel",value:function(t){void 0===this.changedDelta&&(this.changedDelta=0);var e=this.current.scale,i=Math.max(-1,Math.min(1,-t.deltaY||-t.deltaX||t.wheelDelta||-t.detail));if(i<0&&e<=this.option("minScale")||i>0&&e>=this.option("maxScale")){if(this.changedDelta+=Math.abs(i),this.changedDelta>this.option("wheelLimit"))return}else this.changedDelta=0;e=e*(100+i*this.option("wheelFactor"))/100,t.preventDefault();var n=this.$content.getClientRects()[0],o=n.top,s=n.left,a=t.clientX-s,r=t.clientY-o;this.zoomTo(e,{x:a,y:r})}},{key:"onClick",value:function(t){var e=this;if(!t.defaultPrevented){if(window.getSelection().toString().length)return t.stopPropagation(),void t.stopImmediatePropagation();if(this.drag.startPosition&&this.drag.endPosition&&(Math.abs(this.drag.endPosition.top-this.drag.startPosition.top)>1||Math.abs(this.drag.endPosition.left-this.drag.startPosition.left)>1))return t.stopPropagation(),void t.stopImmediatePropagation();if(this.drag.distance>(this.lockAxis?6:1))return t.preventDefault(),t.stopPropagation(),void t.stopImmediatePropagation();var i=null,n=null;void 0!==t.clientX&&void 0!==t.clientY&&(i=t.clientX-this.$content.getClientRects()[0].left,n=t.clientY-this.$content.getClientRects()[0].top);var o=this.options.doubleClick;if(!o&&this.events.doubleClick&&this.events.doubleClick.length&&(o=!0),o){if(!this.clickTimer)return this.lastClickEvent=t,void(this.clickTimer=setTimeout((function(){e.clickTimer=null,!1!==e.trigger("click",t)&&"toggleZoom"===e.option("click")&&e.toggleZoom({x:i,y:n})}),this.option("clickDelay")));this.getDistance([t,this.lastClickEvent])>=6||(clearTimeout(this.clickTimer),this.clickTimer=null,!1!==this.trigger("doubleClick",t)&&"toggleZoom"===this.option("doubleClick")&&this.toggleZoom({x:i,y:n}))}else{if(!1===this.trigger("click",t))return;"toggleZoom"===this.option("click")&&this.toggleZoom({x:i,y:n})}}}},{key:"destroy",value:function(){"destroy"!==this.state&&(this.state="destroy",this.$viewport.classList.remove("not-selectable"),this.$content instanceof HTMLImageElement&&!this.$content.complete&&(this.$content.onload=null,this.$content.onerror=null),this.pendingAnimateUpdate&&(cancelAnimationFrame(this.pendingAnimateUpdate),this.pendingAnimateUpdate=null),this.clickTimer&&(clearTimeout(this.clickTimer),this.clickTimer=null),this.detachEvents(),this.pointers=[],this.resetValues(),this.$viewport=null,this.$content=null,this.options={},this.events={})}}]),n}(k);C.version="4.0.0-alpha.4",C.Plugins={};var P=function(t,e){var i=0;return function(){var n=(new Date).getTime();if(!(n-i1&&this.carousel.elemDimWidth=t-1&&this.$next.setAttribute("disabled","")))}},{key:"cleanup",value:function(){this.$prev&&this.$prev.remove(),this.$prev=null,this.$next&&this.$next.remove(),this.$next=null,this.$container&&this.$container.remove(),this.$container=null}},{key:"attach",value:function(){this.carousel.on("refresh change",this.onRefresh)}},{key:"detach",value:function(){this.carousel.off("refresh change",this.onRefresh),this.cleanup()}}]),t}();S.defaults={prevTpl:'',nextTpl:'',classNames:{main:"carousel__nav",button:"carousel__button",next:"is-next",prev:"is-prev"}};var E=function(){function t(e){i(this,t),this.carousel=e,this.$list=null,this.events={change:this.onChange.bind(this),refresh:this.onRefresh.bind(this)}}return o(t,[{key:"buildList",value:function(){var t=this;if(!(this.carousel.pages.length<2)){var e=document.createElement("ol");return e.classList.add("carousel__dots"),e.addEventListener("click",(function(e){if("page"in e.target.dataset){e.preventDefault(),e.stopPropagation();var i=parseInt(e.target.dataset.page,10),n=t.carousel;i!==n.page&&(n.pages.length<3&&n.option("infinite")?n[0==i?"slidePrev":"slideNext"]():n.slideTo(i))}})),this.$list=e,this.carousel.$element.appendChild(e),this.carousel.$element.classList.add("has-dots"),e}}},{key:"removeList",value:function(){this.$list&&(this.$list.parentNode.removeChild(this.$list),this.$list=null)}},{key:"rebuildDots",value:function(){var t=this,e=this.$list,i=!!e,n=this.carousel.pages.length;if(n<2)i&&this.removeList();else{i||(e=this.buildList());var o=this.$list.children.length;if(o>n)for(var s=n;s1&&void 0!==arguments[1]?arguments[1]:{};return i(this,n),s=m(!0,{},M,s),(o=e.call(this,s)).state="init",o.$element=t,t.Carousel=l(o),o.page=o.pageIndex=null,o.prevPage=o.prevPageIndex=null,o.slideNext=P(o.slideNext.bind(l(o)),250),o.slidePrev=P(o.slidePrev.bind(l(o)),250),o.attachPlugins(n.Plugins),o.trigger("init"),o.initLayout(),o.initSlides(),o.initPanzoom(),o.state="ready",o.trigger("ready"),o}return o(n,[{key:"initLayout",value:function(){if(!(this.$element instanceof HTMLElement))throw new Error("No root element provided");var t,e,i=this.option("classNames");(this.$viewport=this.option("viewport")||this.$element.querySelector("."+i.viewport),this.$viewport)||(this.$viewport=document.createElement("div"),this.$viewport.classList.add(i.viewport),(t=this.$viewport).append.apply(t,u(this.$element.childNodes)),this.$element.appendChild(this.$viewport));(this.$track=this.option("track")||this.$element.querySelector("."+i.track),this.$track)||(this.$track=document.createElement("div"),this.$track.classList.add(i.track),(e=this.$track).append.apply(e,u(this.$viewport.childNodes)),this.$viewport.appendChild(this.$track))}},{key:"initSlides",value:function(){var t=this;this.slides=[],this.$viewport.querySelectorAll("."+this.option("classNames.slide")).forEach((function(e){var i={$el:e,isDom:!0};t.slides.push(i),t.trigger("createSlide",i,t.slides.length)})),Array.isArray(this.options.slides)&&(this.slides=m(!0,u(this.slides),this.options.slides))}},{key:"updatePage",value:function(){var t=this.page;null===t&&(t=this.page=this.option("initialPage")),this.updateMetrics();var e=this.pages;e[t]||(t=e.length?e[e.length-1].index:0),this.slideTo(t,{friction:0})}},{key:"updateBounds",value:function(){var t=this.Panzoom,e=this.option("infinite"),i=this.option("infiniteX",e),n=this.option("infiniteY",e);i&&(t.boundX=null),n&&(t.boundY=null),i||n||(t.boundX={from:-1*this.pages[this.pages.length-1].left,to:-1*this.pages[0].left})}},{key:"initPanzoom",value:function(){var t=this,e=m(!0,{},{content:this.$track,click:!1,doubleClick:!1,wheel:!1,pinchToZoom:!1,lockAxis:"x",textSelection:function(){return t.option("textSelection",!1)},panOnlyZoomed:function(){return t.option("panOnlyZoomed",t.elemDimWidth1?i-1:0),o=1;o1&&t.drag.elapsedTime<350&&Math.abs(t.drag.distanceY)<1&&Math.abs(t.drag.distanceX)>5)this[t.drag.distanceX<0?"slideNext":"slidePrev"]();else if(e){var i=d(this.getPageFromPosition(-1*this.Panzoom.pan.x),2)[1];this.setPage(i)}else this.slideToClosest()}},{key:"manageInfiniteTrack",value:function(){if(!(!this.option("infiniteX",this.option("infinite"))||this.pages.length<2||this.elemDimWidtht.viewportDim.width&&(t.current.x-=t.contentDim.width,t.drag.firstPosition&&(t.drag.firstPosition.x-=t.contentDim.width),this.pageIndex=this.pageIndex+this.pages.length,e=!0),e&&"dragging"===t.state&&t.resetDragState(),e}}},{key:"manageSlideVisiblity",value:function(){var t=this,e=this.elemDimWidth,i=this.wrapDimWidth,n=-1*this.Panzoom.current.x;Math.abs(n)<.1&&(n=0);var o=this.option("preload"),s=this.option("infiniteX",this.option("infinite")),a=parseFloat(window.getComputedStyle(this.$viewport,null).getPropertyValue("padding-left")),r=parseFloat(window.getComputedStyle(this.$viewport,null).getPropertyValue("padding-right"));this.slides.forEach((function(l){var c,h,d=0;c=n-a,h=n+i+r,c-=o*(i+a+r),h+=o*(i+a+r);var u=l.left+l.width>c&&l.leftc&&l.leftc&&l.leftn&&l.left<=n+i+r&&(d=0)):t.removeSlideEl(l),l.hasDiff=d}));var l=0,c=0;this.slides.forEach((function(t,i){var n=0;t.$el?(i!==l||t.hasDiff?n=c+t.hasDiff*e:c=0,t.$el.style.left=Math.abs(n)>.1?"".concat(c+t.hasDiff*e,"px"):"",l++):c+=t.width})),this.Panzoom.viewportDim.height=this.Panzoom.$content.clientHeight,this.markSelectedSlides()}},{key:"markSelectedSlides",value:function(){var t=this,e=this.option("classNames.slideSelected"),i="aria-hidden";this.slides.forEach((function(n,o){var s=n.$el;if(s){var a=t.pages[t.page];a&&a.indexes&&a.indexes.indexOf(o)>-1?(e&&!s.classList.contains(e)&&(s.classList.add(e),t.trigger("selectSlide",n)),s.removeAttribute(i)):(e&&s.classList.contains(e)&&(s.classList.remove(e),t.trigger("unselectSlide",n)),s.setAttribute(i,!0))}}))}},{key:"createSlideEl",value:function(t){if(t){if(!t.$el){var e,i=document.createElement("div");if(i.dataset.index=t.index,i.classList.add(this.option("classNames.slide")),t.customClass)(e=i.classList).add.apply(e,u(t.customClass.split(" ")));t.html&&(i.innerHTML=t.html);var n=[];this.slides.forEach((function(t,e){t.$el&&n.push(e)}));var o=t.index,s=null;if(n.length){var a=n.reduce((function(t,e){return Math.abs(e-o)this.wrapDimWidth)for(var l=0;lthis.wrapDimWidth)&&(a.push({indexes:[],slides:[]}),c=a.length-1,h=0),h+=f.width,a[c].indexes.push(d),a[c].slides.push(f)}var v=this.option("center"),p=this.option("fill");a.forEach((function(t,i){t.index=i,t.width=t.slides.reduce((function(t,e){return t+e.width}),0),t.left=t.slides[0].left,v&&(t.left+=.5*(e.wrapDimWidth-t.width)*-1),p&&!e.option("infiniteX",e.option("infinite"))&&e.elemDimWidth>e.wrapDimWidth&&(t.left=Math.max(t.left,0),t.left=Math.min(t.left,e.elemDimWidth-e.wrapDimWidth))}));var g,m=[];a.forEach((function(t){g&&t.left===g.left?(g.width+=t.width,g.slides=[].concat(u(g.slides),u(t.slides)),g.indexes=[].concat(u(g.indexes),u(t.indexes))):(t.index=m.length,g=t,m.push(t))})),this.pages=m,this.manageSlideVisiblity(),this.trigger("refresh")}},{key:"setPage",value:function(t,e){var i=0,n=parseInt(t,10)||0,o=this.page,s=this.pageIndex,a=this.pages.length;if(t=(n%a+a)%a,this.option("infiniteX",this.option("infinite"))&&this.elemDimWidth>this.wrapDimWidth){var r=Math.floor(n/a)||0,l=this.elemDimWidth;if(i=this.pages[t].left+r*l,!0===e&&a>2){var c=-1*this.Panzoom.current.x,h=i-l,d=i+l,u=Math.abs(c-i),f=Math.abs(c-h),v=Math.abs(c-d);v1&&void 0!==arguments[1]?arguments[1]:{},i=e.friction,n=void 0===i?this.option("friction"):i;this.Panzoom.panTo({x:-1*this.setPage(t,!0),y:0,friction:n})}},{key:"slideToClosest",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=this.getPageFromPosition(-1*this.Panzoom.pan.x),i=d(e,2),n=i[1];this.slideTo(n,t)}},{key:"slideNext",value:function(){this.slideTo(this.pageIndex+1)}},{key:"slidePrev",value:function(){this.slideTo(this.pageIndex-1)}},{key:"getPageforSlide",value:function(t){var e=this.pages.find((function(e){return e.indexes.indexOf(t)>-1}));return e?e.index:null}},{key:"getPageFromPosition",value:function(t){var e=this.pages.length;this.option("center")&&(t+=.5*this.wrapDimWidth);var i=Math.floor(t/this.elemDimWidth);t-=i*this.elemDimWidth;var n=this.slides.find((function(e){return e.leftt}));if(n){var o=this.getPageforSlide(n.index);return[o,o+i*e]}return[0,0]}},{key:"removeSlideEl",value:function(t){t.$el&&!t.isDom&&(this.trigger("deleteSlide",t),t.$el.remove(),t.$el=null)}},{key:"destroy",value:function(){var t=this;this.state="destroy",this.slides.forEach((function(e){t.removeSlideEl(e)})),this.Panzoom.destroy(),this.options={},this.events={}}}]),n}(k);D.version="4.0.0-alpha.4",D.Plugins=T;var A=!("undefined"==typeof window||!window.document||!window.document.createElement),z=function(){function t(e){i(this,t),this.fancybox=e,this.viewport=null,this.pendingUpdate=null;for(var n=0,o=["onReady","onResize","onTouchstart","onTouchmove"];n.1&&(o="".concat(e.width*i,"px"),s="".concat(e.height*i,"px"),a="translate3d(".concat(e.offsetLeft,"px, ").concat(e.offsetTop,"px, 0) scale(").concat(1/i,")")),n.style.width=o,n.style.height=s,n.style.transform=a}}},{key:"onTouchstart",value:function(t){this.startY=t.touches?t.touches[0].screenY:t.screenY}},{key:"onTouchmove",value:function(t){var e=this.startY,i=window.innerWidth/window.document.documentElement.clientWidth;if(!(t.touches.length>1||1!==i)){var n=t.target,o=w(n);if(o){var s=window.getComputedStyle(o),a=parseInt(s.getPropertyValue("height"),10),r=t.touches?t.touches[0].screenY:t.screenY,l=e<=r&&0===o.scrollTop,c=e>=r&&o.scrollHeight-o.scrollTop===a;(l||c)&&t.preventDefault()}else t.preventDefault()}}},{key:"cleanup",value:function(){this.pendingUpdate&&(cancelAnimationFrame(this.pendingUpdate),this.pendingUpdate=null);var t=this.viewport;t&&(t.removeEventListener("resize",this.onResize),this.viewport=null),window.removeEventListener("touchstart",this.onTouchstart,!1),window.removeEventListener("touchmove",this.onTouchmove,!1)}},{key:"attach",value:function(){this.fancybox.on("initLayout",this.onReady)}},{key:"detach",value:function(){this.fancybox.off("initLayout",this.onReady),this.cleanup()}}]),t}(),I=function(){function t(e){i(this,t),this.fancybox=e,this.$wrap=null,this.state="init";for(var n=0,o=["onReady","onClosing","onKeydown"];n'),customClass:"has-thumb has-".concat(e.type||"image")})})),t}},{key:"toggle",value:function(){return"ready"===this.state?(this.Carousel.Panzoom.detachEvents(),this.$wrap.style.display="none",void(this.state="hidden")):"hidden"===this.state?(this.$wrap.style.display="",this.Carousel.Panzoom.attachEvents(),void(this.state="ready")):void this.initLayout()}},{key:"cleanup",value:function(){this.Carousel&&(this.Carousel.destroy(),this.Carousel=null),this.$wrap&&(this.$wrap.remove(),this.$wrap=null),this.state="init"}},{key:"attach",value:function(){this.fancybox.on(this.events)}},{key:"detach",value:function(){this.fancybox.off(this.events),this.cleanup()}}]),t}();I.defaults={autoStart:!0,minSlideCount:3,key:"t"};var _=function(t){return Object.entries(t).map((function(t){return t.map(encodeURIComponent).join("=")})).join("&")},R=function(){function t(e){i(this,t),this.fancybox=e;for(var n=0,o=["onPrepare","onCreateSlide","onDeleteSlide","onSelectSlide","onUnselectSlide","onRefresh","onMessage"];n0?"svembed":"embed"),i="map"):(n=e.match(/(?:maps\.)?google\.([a-z]{2,3}(?:\.[a-z]{2})?)\/(?:maps\/search\/)(.*)/i))&&(t.src="//maps.google.".concat(n[1],"/maps?q=").concat(n[2].replace("query=","q=").replace("api=1",""),"&output=embed"),i="map");i||("#"===e.charAt(0)?i="inline":(n=e.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))?(i="html5video",t.format=t.format||"video/"+("ogv"===n[1]?"ogg":n[1])):e.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)?i="image":e.match(/\.(pdf)((\?|#).*)?$/i)&&(i="pdf")),t.type=i||this.fancybox.option("defaultType","image"),"html5video"!==i&&"video"!==i||(t.video=m({},this.fancybox.option("Html.video"),t.video),t.width&&t.height?t.ratio=parseFloat(t.width)/parseFloat(t.height):t.ratio=t.ratio||t.video.ratio)}}},{key:"loadInlineContent",value:function(t){var e;if(t.src instanceof HTMLElement)e=t.src;else if("string"==typeof t.src){var i=t.src.split("#",2),n=2===i.length&&""===i[0]?i[1]:i[0];e=document.getElementById(n)}if(e){if("clone"===t.type||e.$placeHolder){var o=(e=e.cloneNode(!0)).getAttribute("id");o=o?"".concat(o,"--clone"):"clone-".concat(this.fancybox.id,"-").concat(t.index),e.setAttribute("id",o)}else{var s=document.createElement("div");s.classList.add("fancybox-placeholder"),e.parentNode.insertBefore(s,e),e.$placeHolder=s}this.fancybox.setContent(t,e)}else this.fancybox.setError(t,"{{ELEMENT_NOT_FOUND}}")}},{key:"loadAjaxContent",value:function(t){var e=this.fancybox,i=new XMLHttpRequest;e.showLoading(t),i.onreadystatechange=function(){i.readyState===XMLHttpRequest.DONE&&"ready"===e.state&&(e.hideLoading(t),200===i.status?e.setContent(t,i.responseText):e.setError(t,404===i.status?"{{AJAX_NOT_FOUND}}":"{{AJAX_FORBIDDEN}}"))},i.open("GET",t.src),i.send(t.ajax||null),t.xhr=i}},{key:"loadIframeContent",value:function(t){var e=this,i=this.fancybox,n=document.createElement("iframe");if(n.className="fancybox__iframe",n.setAttribute("id","fancybox__iframe_".concat(i.id,"_").concat(t.index)),n.setAttribute("allow","autoplay; fullscreen"),n.setAttribute("scrolling","auto"),t.$iframe=n,"iframe"!==t.type||!1===t.preload)return n.setAttribute("src",t.src),void this.fancybox.setContent(t,n);i.showLoading(t);var o=document.createElement("div");o.style.visibility="hidden",this.fancybox.setContent(t,o),o.appendChild(n),n.onerror=function(){i.setError(t,"{{IFRAME_ERROR}}")},n.onload=function(){i.hideLoading(t);var o=!1;"yes"!==n.dataset.ready&&(n.dataset.ready="yes",o=!0),n.src.length&&(n.parentNode.style.visibility="",!1!==t.autoSize&&e.autoSizeIframe(n),o&&i.revealContent(t))},n.setAttribute("src",t.src)}},{key:"setAspectRatio",value:function(t){var e=t.ratio;if(e&&t.$content){t.$content.style.maxWidth="",t.$content.style.maxHeight="";var i=t.$content.offsetWidth,n=t.$content.offsetHeight,o=t.width,s=t.height;if(o&&s&&(i>o||n>s)){var a=Math.min(o/i,s/n);i*=a,n*=a}e\n \n Sorry, your browser doesn\'t support embedded videos, download and watch with your favorite video player!\n',format:""}};var O=function(t){var e=t.naturalWidth,i=t.naturalHeight,n=t.width,o=t.height,s=e/i,a={width:n,height:o};return s>n/o?a.height=n/s:a.width=o*s,a.left=.5*(n-a.width),a.right=e+a.left,a},N=function(){function t(e){i(this,t),this.fancybox=e;for(var n=0,o=["onReady","onClosing","onPageChange","onCreateSlide","onRemoveSlide","onRefresh","onImageStatusChange"];n.1),{top:l,left:c,scale:e.width/a,opacity:h}}},{key:"zoomIn",value:function(){var t=this.fancybox;if("init"!==t.Carousel.state){var e=t.getSlide(),i=e.Panzoom,n=this.getZoomInfo(e),o=n.top,s=n.left,a=n.scale,r=n.opacity;e.state="zoomIn",i.detachEvents(),t.trigger("reveal",e),i.panTo({x:-1*s,y:-1*o,scale:a,friction:0,ignoreBounds:!0}),e.$content.style.visibility="",!0===r&&i.on("afterTransform",(function(t){"zoomIn"!==e.state&&"zoomOut"!==e.state||(t.$content.style.opacity=Math.min(1,t.current.scale))})),i.panTo({x:0,y:0,scale:1,friction:this.fancybox.option("Image.zoomFriction")})}}},{key:"zoomOut",value:function(){var t=this,e=this.fancybox,i=e.getSlide(),n=i.Panzoom;if(n){i.state="zoomOut",e.state="customClosing",i.$caption&&(i.$caption.style.visibility="hidden");var o=.75*this.fancybox.option("Image.zoomFriction"),s=function(){var e=t.getZoomInfo(i),s=e.top,a=e.left,r=e.scale;n.panTo({x:-1*a,y:-1*s,scale:r,ignoreBounds:!0,friction:o}),o*=.98};window.addEventListener("scroll",s),n.on("afterAnimate",(function(){window.removeEventListener("scroll",s),e.destroy()})),s()}}},{key:"handleCursor",value:function(t){var e=t.Panzoom,i=this.fancybox.option("Image.click"),n=t.$el.classList;e&&"toggleZoom"===i?n[e&&1===e.current.scale&&e.option("maxScale")-e.current.scale>.01?"add":"remove"](this.fancybox.option("Image.canZoomInClass")):"close"===i&&n.add(this.fancybox.option("Image.canZoomOutClass"))}},{key:"onWheel",value:function(t,e){switch(this.fancybox.option("Image.wheel")){case"zoom":t.zoomWithWheel(e);break;case"close":this.fancybox.close();break;case"slide":this.fancybox[e.deltaY<0?"prev":"next"]()}e.preventDefault()}},{key:"onClick",value:function(t,e){if(!(this.fancybox.Carousel.Panzoom.drag.distance>=6||this.fancybox.Carousel.Panzoom.lockAxis||"IMG"!=e.target.tagName&&!e.target.classList.contains("fancybox__content")))switch(e.preventDefault(),e.stopPropagation(),this.fancybox.option("Image.click")){case"toggleZoom":var i=e.clientX-t.$content.getClientRects()[0].left,n=e.clientY-t.$content.getClientRects()[0].top;t.toggleZoom({x:i,y:n});break;case"close":this.fancybox.close();break;case"next":this.fancybox.next();break;case"prev":this.fancybox.prev()}}},{key:"onRefresh",value:function(t,e){var i=this;e.slides.forEach((function(t){t.Panzoom&&i.updateDimensions(t)}))}},{key:"onRemoveSlide",value:function(t,e,i){i.$image&&(i.$el.classList.remove(t.option("Image.canZoomInClass")),i.$image.onload=i.$image.onerror=null,i.$image.remove(),i.$image=null),i.Panzoom&&(i.Panzoom.destroy(),i.Panzoom=null),delete i.$el.dataset.imageFit}},{key:"onClosing",value:function(t){t.Carousel.slides.forEach((function(t){t.$image&&(t.$image.onload=t.$image.onerror=null),t.Panzoom&&t.Panzoom.detachEvents()})),"closing"===this.fancybox.state&&this.canZoom(t.getSlide())&&this.zoomOut()}},{key:"onPageChange",value:function(t,e){var i=this,n=t.getSlide();e.slides.forEach((function(t){t.Panzoom&&"done"===t.state&&(t.index!==n.index?t.Panzoom.panTo({x:0,y:0,scale:1,friction:.8}):0===e.Panzoom.velocity.x&&i.revealContent(t))}))}},{key:"attach",value:function(){this.fancybox.on(this.events)}},{key:"detach",value:function(){this.fancybox.off(this.events)}}]),t}();N.defaults={Panzoom:{maxScale:1},canZoomInClass:"can-zoom_in",canZoomOutClass:"can-zoom_out",zoom:!0,zoomOpacity:"auto",zoomFriction:.8,ignoreCoveredThumbnail:!1,click:"toggleZoom",doubleClick:null,wheel:"zoom",fit:"contain"};var F=function(){var t=window.location.hash.substr(1),e=t.split("-"),i=e.length>1&&/^\+?\d+$/.test(e[e.length-1])&&parseInt(e.pop(-1),10)||null;return{hash:t,slug:e.join("-"),index:i}},B={ScrollLock:z,Thumbs:I,Html:R,Image:N,Hash:function(){function t(e){i(this,t),this.fancybox=e,this.events={closing:this.onClosing.bind(this),"Carousel.ready Carousel.change":this.onChange.bind(this)},this.hasCreatedHistory=!1,this.origHash="",this.timer=null}return o(t,[{key:"onChange",value:function(t,e){var i=this;this.timer&&clearTimeout(this.timer);var n=null===e.prevPage,o=t.getSlide(),s=o.$trigger&&o.$trigger.dataset,a=window.location.hash.substr(1),r=!1;if(o.slug)r=o.slug;else{var l=s&&s.fancybox;l&&l.length&&"true"!==l&&(r=l+(e.slides.length>1?"-"+(o.index+1):""))}n&&(this.origHash=a!==r?this.origHash:""),r&&a!==r&&(this.timer=setTimeout((function(){try{window.history[n?"pushState":"replaceState"]({},document.title,window.location.pathname+window.location.search+"#"+r),n&&(i.hasCreatedHistory=!0)}catch(t){}}),300))}},{key:"onClosing",value:function(){if(this.timer&&clearTimeout(this.timer),!0!==this.hasSilentClose){if(!this.hasCreatedHistory)try{return void window.history.replaceState({},document.title,window.location.pathname+window.location.search+(this.origHash?"#"+this.origHash:""))}catch(t){}window.history.back()}}},{key:"attach",value:function(t){t.on(this.events)}},{key:"detach",value:function(t){t.off(this.events)}}],[{key:"startFromUrl",value:function(){if(!t.Fancybox.getInstance()){var e=F(),i=e.hash,n=e.slug,o=e.index;if(n){var s=document.querySelector('[data-slug="'.concat(i,'"]'));if(s&&s.dispatchEvent(new CustomEvent("click",{bubbles:!0,cancelable:!0})),!t.Fancybox.getInstance()){var a=document.querySelectorAll('[data-fancybox="'.concat(n,'"]'));a.length&&(null===o&&1===a.length?s=a[0]:o&&(s=a[o-1]),s&&s.dispatchEvent(new CustomEvent("click",{bubbles:!0,cancelable:!0})))}}}}},{key:"onHashChange",value:function(){var e=F(),i=e.slug,n=e.index,o=t.Fancybox.getInstance();if(o){if(i){var s,a=o.Carousel,r=p(a.slides);try{for(r.s();!(s=r.n()).done;){var l=s.value;if(l.slug&&l.slug===i)return a.slideTo(l.index)}}catch(t){r.e(t)}finally{r.f()}var c=o.getSlide(),h=c.$trigger&&c.$trigger.dataset;if(h&&h.fancybox===i)return a.slideTo(n-1)}o.plugins.Hash.hasSilentClose=!0,o.close()}t.startFromUrl()}},{key:"onReady",value:function(){window.addEventListener("hashchange",t.onHashChange,!1),t.startFromUrl()}},{key:"create",value:function(){A&&window.requestAnimationFrame((function(){t.onReady()}))}},{key:"destroy",value:function(){window.removeEventListener("hashchange",t.onHashChange,!1)}}]),t}()},W=0,H=null,U=function(t){s(n,t);var e=h(n);function n(t){var o,s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};i(this,n);var a,r=function(t,e){var i=m(!0,{},t[e.startIndex]||{});return t.forEach((function(t){var e=t.$trigger;if(e){var i=e.dataset||{};t.src=i.src||e.getAttribute("href")||t.src,t.type=i.type||t.type}})),m(!0,{},n.defaults,e,i)};return a=!1,document.createElement("div").focus({get preventScroll(){return a=!0,!1}}),H=a,(o=e.call(this,r(t,s))).state="init",o.items=t,o.bindHandlers(),o.attachPlugins(n.Plugins),o.trigger("init"),!0===o.option("hideScrollbar")&&o.hideScrollbar(),o.initLayout(),o.initCarousel(o.getSlides()),o.attachEvents(),o.state="ready",o.trigger("ready"),o.$container.setAttribute("aria-hidden","false"),o}return o(n,[{key:"bindHandlers",value:function(){for(var t=0,e=["onMousedown","onKeydown","onClick","onCreateSlide","onSettle","onTouchMove","onTouchEnd","onTransform"];t1?"x":"";return e.options.dragToClose&&(t+="y"),t}},on:{"*":function(t){for(var i=arguments.length,n=new Array(i>1?i-1:0),o=1;o=150||Math.abs(e)>=35&&t.drag.elapsedTime<350)&&(this.option("hideClass")&&(this.getSlide().hideClass="fancybox-throwOut".concat(t.current.y<0?"Up":"Down")),this.close())}},{key:"onTransform",value:function(t){if(this.$backdrop){var e=Math.abs(t.current.y),i=e<1?"":Math.max(0,Math.min(1,1-e/t.$content.clientHeight*1.5));this.$container.style.setProperty("--fancybox-ts",i?"0s":""),this.$container.style.setProperty("--fancybox-opacity",i)}}},{key:"onMousedown",value:function(){document.body.classList.add("is-using-mouse")}},{key:"onKeydown",value:function(t){if(n.getInstance().id===this.id){document.body.classList.remove("is-using-mouse");var e=t.key;if("Tab"===e&&this.option("trapFocus"))this.focus(t);else{var i=this.option("keyboard");if(i&&!t.ctrlKey&&!t.altKey&&!t.shiftKey){var o=document.activeElement&&document.activeElement.classList,s=o&&o.contains("carousel__button");if("Escape"!==e&&!s)if(t.target.isContentEditable||-1!==["BUTTON","TEXTAREA","OPTION","INPUT","SELECT","VIDEO"].indexOf(t.target.nodeName))return;if(!1!==this.trigger("keydown",e)){"Enter"!==e&&t.preventDefault();var a=i[e];"function"==typeof this[a]&&this[a]()}}}}}},{key:"getSlide",value:function(){var t=this.Carousel;if(!t)return null;var e=null===t.page?t.option("initialPage"):t.page,i=t.pages||[];return i.length&&i[e]?i[e].slides[0]:null}},{key:"focus",value:function(t){var e=function(t){t.setActive?t.setActive():H?t.focus({preventScroll:!0}):t.focus()};t&&t.preventDefault();var i=this.getSlide().$el;i.tabIndex=0;var n,o=[],s=p([].slice.call(this.$container.querySelectorAll(["a[href]","area[href]",'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',"select:not([disabled]):not([aria-hidden])","textarea:not([disabled]):not([aria-hidden])","button:not([disabled]):not([aria-hidden])","iframe","object","embed","video","audio","[contenteditable]",'[tabindex]:not([tabindex^="-"]):not([disabled]):not([aria-hidden])'])));try{for(s.s();!(n=s.n()).done;){var a=n.value;if(!a.classList||!a.classList.contains("fancybox__slide")){var r=a.closest(".fancybox__slide");r?r===i&&o[a.hasAttribute("autofocus")?"unshift":"push"](a):o.push(a)}}}catch(t){s.e(t)}finally{s.f()}if(o.length){this.Carousel.pages.length>1&&o.push(i);var l=o.indexOf(document.activeElement),c=t&&!t.shiftKey,h=t&&t.shiftKey;return c?l===o.length-1?e(o[0]):e(o[l+1]):h?e(0===l?o[o.length-1]:o[l-1]):l<0?e(o[0]):void 0}}},{key:"hideScrollbar",value:function(){if(A){var t=window.innerWidth-document.documentElement.getBoundingClientRect().width,e="fancybox-style-noscroll",i=document.getElementById(e);i||t&&((i=document.createElement("style")).id=e,i.type="text/css",i.innerHTML=".compensate-for-scrollbar {padding-right: ".concat(t,"px;}"),document.getElementsByTagName("head")[0].appendChild(i),document.body.classList.add("compensate-for-scrollbar"))}}},{key:"revealScrollbar",value:function(){document.body.classList.remove("compensate-for-scrollbar");var t=document.getElementById("fancybox-style-noscroll");t&&t.remove()}},{key:"clearContent",value:function(t){this.Carousel.trigger("deleteSlide",t),t.$content&&(t.$content.remove(),t.$content=null),t._className&&t.$el.classList.remove(t._className)}},{key:"setContent",value:function(t,e){var i,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=t.$el;if(e instanceof HTMLElement?["img","iframe","video","audio"].indexOf(e.nodeName.toLowerCase())>-1?(i=document.createElement("div")).appendChild(e):i=e:(i=document.createElement("div")).innerHTML=e,!(i instanceof Element))throw new Error("Element expected");return t._className="has-".concat(n.suffix||t.type||"unknown"),o.classList.add(t._className),i.classList.add("fancybox__content"),"none"!==i.style.display&&"none"!==window.getComputedStyle(i).getPropertyValue("display")||(i.style.display="flex"),t.id&&i.setAttribute("id",t.id),t.$content=i,o.insertBefore(i,o.querySelector(".fancybox__caption")),this.manageCloseButton(t),"loading"!==t.state&&this.revealContent(t),i}},{key:"manageCloseButton",value:function(t){var e=this,i=void 0===t.closeButton?this.option("closeButton"):t.closeButton;if(i&&(!this.$closeButton||"inside"===i)){var n=document.createElement("button");n.classList.add("carousel__button","is-close"),n.setAttribute("title",this.options.l10n.CLOSE),n.innerHTML=this.option("template.closeButton"),n.addEventListener("click",(function(t){return e.close(t)})),"inside"===i?(t.$closeButton&&t.$closeButton.remove(),t.$closeButton=t.$content.appendChild(n)):this.$closeButton=this.$container.insertBefore(n,this.$container.firstChild)}}},{key:"revealContent",value:function(t){var e=this;this.trigger("reveal",t),t.$content.style.visibility="";var i=!1;"error"!==t.state&&"ready"!==t.state&&null===this.Carousel.prevPage&&t.index===this.options.startIndex&&(i=void 0===t.showClass?this.option("showClass"):t.showClass),i?(t.state="animating",this.animateCSS(t.$content,i,(function(){e.done(t)}))):this.done(t)}},{key:"animateCSS",value:function(t,e,i){if(t&&t.dispatchEvent(new CustomEvent("animationend",{bubbles:!0,cancelable:!0})),t&&e){t.addEventListener("animationend",(function n(o){o.currentTarget===this&&(t.classList.remove(e),t.removeEventListener("animationend",n),i&&i())})),t.classList.add(e)}else"function"==typeof i&&i()}},{key:"done",value:function(t){if("init"===this.state||"ready"===this.state){t.state="done",this.trigger("done",t);var e=this.getSlide();e&&t.index===e.index&&this.option("autoFocus")&&this.focus()}}},{key:"setError",value:function(t,e){t.state="error",this.hideLoading(t),this.clearContent(t);var i=document.createElement("div");i.classList.add("fancybox-error"),i.innerHTML=this.localize(e||"

{{ERROR}}

"),this.setContent(t,i,{suffix:"error"})}},{key:"showLoading",value:function(t){var e=this;t.state="loading",this.trigger("load",t),t.$el.classList.add("is-loading");var i=t.$el.querySelector(".fancybox__spinner");i||((i=document.createElement("div")).classList.add("fancybox__spinner"),i.innerHTML=this.option("template.spinner"),i.addEventListener("click",(function(){e.Carousel.Panzoom.velocity||e.close()})),t.$el.insertBefore(i,t.$el.firstChild))}},{key:"hideLoading",value:function(t){var e=t.$el&&t.$el.querySelector(".fancybox__spinner");e&&(e.remove(),t.$el.classList.remove("is-loading")),"loading"===t.state&&(t.state="ready")}},{key:"next",value:function(){var t=this.Carousel;t&&t.pages.length>1&&t.slideNext()}},{key:"prev",value:function(){var t=this.Carousel;t&&t.pages.length>1&&t.slidePrev()}},{key:"jumpTo",value:function(){var t;this.Carousel&&(t=this.Carousel).slideTo.apply(t,arguments)}},{key:"close",value:function(t){var e=this;if(t&&t.preventDefault(),!(["closing","customClosing","destroy"].indexOf(this.state)>-1)&&!1!==this.trigger("shouldClose",t)&&(this.state="closing",this.Carousel.Panzoom.destroy(),this.detachEvents(),this.trigger("closing",t),"destroy"!==this.state)){this.$container.setAttribute("aria-hidden","true"),this.$container.classList.add("is-closing");var i=this.getSlide();if(this.Carousel.slides.forEach((function(t){t.$content&&t.index!==i.index&&t.$content.remove()})),"closing"===this.state){var n=void 0===i.hideClass?this.option("hideClass"):i.hideClass;this.animateCSS(i.$content,n,(function(){e.destroy()}))}}}},{key:"destroy",value:function(){this.state="destroy",this.trigger("destroy");var t=this.option("placeFocusBack")?this.getSlide().$trigger:null;if(this.Carousel.destroy(),this.detachPlugins(),this.Carousel=null,this.options={},this.events={},this.$container.remove(),this.$container=this.$backdrop=this.$carousel=null,t)if(H)t.focus({preventScroll:!0});else{var e=document.body.scrollTop;t.focus(),document.body.scrollTop=e}var i=n.getInstance();i?i.focus():(document.documentElement.classList.remove("with-fancybox"),document.body.classList.remove("is-using-mouse"),this.revealScrollbar())}}],[{key:"show",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return new n(t,e)}},{key:"fromEvent",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!t.defaultPrevented&&!(t.button&&0!==t.button||t.ctrlKey||t.metaKey||t.shiftKey)){var i,o,s,a=!1,r=t.target;if((r.matches("[data-fancybox-trigger]")||(r=r.closest("[data-fancybox-trigger]")))&&(s=r&&r.dataset&&r.dataset.fancyboxTrigger),s){var l=document.querySelectorAll('[data-fancybox="'.concat(s,'"]')),c=parseInt(r.dataset.fancyboxIndex,10)||0;r=l.length?l[c]:r}r||(r=t.target),Array.from(n.openers.keys()).reverse().some((function(e){if((i=r).matches(e)||(i=i.closest(e)))return t.preventDefault(),o=e,!0})),o&&(e.target=i,i.origTarget=t.target,a=n.fromOpener(o,e));var h=n.getInstance();return h&&"ready"===h.state&&t.detail&&document.body.classList.add("is-using-mouse"),a}}},{key:"fromOpener",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=function(t){for(var e=["false","0","no","null"],i=["true","1","yes"],n=Object.assign({},t.dataset),o=0,s=Object.entries(n);o-1)n[r]=!1;else if(i.indexOf(n[r])>-1)n[r]=!0;else try{n[r]=JSON.parse(l)}catch(t){n[r]=l}}return delete n.fancybox,delete n.type,t instanceof Element&&(n.$trigger=t),n},o=[],s=e.startIndex||0,a=(e=m({},e,n.openers.get(t))).groupAttr;void 0===a&&(a="data-fancybox");var r=e.target;if(a){if(r&&t&&t==="[".concat(a,"]")){var l=r.getAttribute("".concat(a));t=!(!l||!l.length||"true"===l)&&"[".concat(a,"='").concat(l,"']")}}else t=!1;if(t&&(o=[].slice.call(document.querySelectorAll(t))),!o.length&&r&&(o=[r]),!o.length)return!1;var c=n.getInstance();return!(c&&o.indexOf(c.options.$trigger)>-1)&&(s=r?o.indexOf(r):s,new n(o=o.map(i),m({},e,{startIndex:s,$trigger:r})))}},{key:"bind",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(A){if(!n.openers.size){document.body.addEventListener("click",n.fromEvent,!1);for(var i=0,o=Object.entries(n.Plugins||{});i0&&void 0!==arguments[0])||arguments[0],e=null;e=n.getInstance();)if(e.close(),!t)return}}]),n}(k);U.version="4.0.0-alpha.4",U.defaults={startIndex:0,preload:1,infinite:!0,showClass:"fancybox-zoomInUp",hideClass:"fancybox-fadeOut",animated:!0,hideScrollbar:!0,parentEl:null,mainClass:null,autoFocus:!0,trapFocus:!0,placeFocusBack:!0,click:"close",closeButton:"inside",dragToClose:!0,keyboard:{Escape:"close",Delete:"close",Backspace:"close",PageUp:"next",PageDown:"prev",ArrowUp:"next",ArrowDown:"prev",ArrowRight:"next",ArrowLeft:"prev"},template:{closeButton:'',spinner:'',main:null},l10n:{CLOSE:"Close",NEXT:"Next",PREV:"Previous",MODAL:"You can close this modal content with the ESC key",ERROR:"Something Went Wrong, Please Try Again Later",IMAGE_ERROR:"Image Not Found",ELEMENT_NOT_FOUND:"HTML Element Not Found",AJAX_NOT_FOUND:"Error Loading AJAX : Not Found",AJAX_FORBIDDEN:"Error Loading AJAX : Forbidden",IFRAME_ERROR:"Error Loading Page"}},U.openers=new Map,U.Plugins=B,U.isMobile=function(){return!!navigator&&/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},U.bind("[data-fancybox]"),t.Carousel=D,t.Fancybox=U,t.Panzoom=C})); diff --git a/docs/variables/.pages b/docs/variables/.pages new file mode 100644 index 0000000..9db56c5 --- /dev/null +++ b/docs/variables/.pages @@ -0,0 +1,3 @@ +title: Variabes +nav: + - ... diff --git a/docs/variables/vyos_container.schema.md b/docs/variables/vyos_container.schema.md new file mode 100644 index 0000000..e847d48 --- /dev/null +++ b/docs/variables/vyos_container.schema.md @@ -0,0 +1,322 @@ +# vyos_container + +**Title:** vyos_container + +VyOS Container configuration + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ---------------------------- | ------- | --------------- | ---------- | ---------- | ------------------------------------------------------------------------------------ | +| - [registries](#registries ) | No | array of string | No | - | Unqualified search registries for any image that does not include the registry i ... | +| - [networks](#networks ) | No | object | No | - | Container networks to be managed | +| - [containers](#containers ) | No | object | No | - | Container to be managed | +| | | | | | | + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_container > registries` + +Unqualified search registries for any image that does not include the registry in the image name. + +| Each item of this array must be | Description | +| ------------------------------- | ----------- | +| [items](#registries_items) | - | +| | | + +### vyos_container > registries > items + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_container > networks` + +Container networks to be managed + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| --------------------------------------------------------- | ------- | ------ | ---------- | ---------------------- | ----------------- | +| - [additionalProperties](#networks_additionalProperties ) | No | object | No | In #/$defs/networkItem | - | +| | | | | | | + +### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_container > networks > networkItem` + +**Examples:** + +```yaml +network: services + +``` + +```yaml +description: test +network: services + +``` + +```yaml +description: test +prefix: 10.35.0.0/24 + +``` + +| All of(Requirement) | +| --------------------------------------------------------------- | +| [Base fields](#networks_additionalProperties_allOf_i0) | +| [Network entry fields](#networks_additionalProperties_allOf_i1) | +| | + +#### `vyos_container > networks > Network entry > allOf > Base fields` + +**Title:** Base fields + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| --------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ------------------------------------------------------ | +| - [description](#networks_additionalProperties_allOf_i0_description ) | No | string | No | - | A string with a functional description of the network. | +| | | | | | | + +##### `vyos_container > networks > Network entry > allOf > Base fields > description` + +A string with a functional description of the network. + +#### `vyos_container > networks > Network entry > allOf > Network entry fields` + +**Title:** Network entry fields + +| One of(Option) | +| ---------------------------------------------------------------------------------------- | +| [Network entry with network reference](#networks_additionalProperties_allOf_i1_oneOf_i0) | +| [Network entry with custom prefix](#networks_additionalProperties_allOf_i1_oneOf_i1) | +| | + +##### `vyos_container > networks > Network entry > allOf > Network entry fields > oneOf > Network entry with network reference` + +**Title:** Network entry with network reference + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ---------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ------------------------------------------------------------------------------------ | +| + [network](#networks_additionalProperties_allOf_i1_oneOf_i0_network ) | No | string | No | - | A reference to the key of an entry in the VyOS networks list. The prefix will be ... | +| | | | | | | + +##### `vyos_container > networks > Network entry > allOf > Network entry fields > oneOf > Network entry with network reference > network` + +A reference to the key of an entry in the VyOS networks list. The prefix will be determined automatically. + +##### `vyos_container > networks > Network entry > allOf > Network entry fields > oneOf > Network entry with custom prefix` + +**Title:** Network entry with custom prefix + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ---------------------------------------------------------- | +| + [prefix](#networks_additionalProperties_allOf_i1_oneOf_i1_prefix ) | No | string | No | - | A network prefix to be assigned to this container network. | +| | | | | | | + +##### `vyos_container > networks > Network entry > allOf > Network entry fields > oneOf > Network entry with custom prefix > prefix` + +A network prefix to be assigned to this container network. + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_container > containers` + +Container to be managed + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ----------------------------------------------------------- | ------- | ------ | ---------- | ------------------------ | ----------------- | +| - [additionalProperties](#containers_additionalProperties ) | No | object | No | In #/$defs/containerItem | - | +| | | | | | | + +### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_container > containers > containerItem` + +| All of(Requirement) | +| -------------------------------------------------------------- | +| [Base fields](#containers_additionalProperties_allOf_i0) | +| [Networking fields](#containers_additionalProperties_allOf_i1) | +| | + +#### `vyos_container > containers > Container entry > allOf > Base fields` + +**Title:** Base fields + +**Example:** + +```yaml +env: + TZ: UTC +image: + repository: vyos/vyos-build + tag: crux + +``` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| --------------------------------------------------------------- | ------- | --------------- | ---------- | ---------- | ----------------------------------------------------------------------- | +| + [image](#containers_additionalProperties_allOf_i0_image ) | No | object | No | - | Configures the container image. | +| - [cap-add](#containers_additionalProperties_allOf_i0_cap-add ) | No | array of string | No | - | A list of capabilities to be added to the container. | +| - [env](#containers_additionalProperties_allOf_i0_env ) | No | object | No | - | A dict containing the environment variables to be set in the container. | +| - [ports](#containers_additionalProperties_allOf_i0_ports ) | No | object | No | - | A dict containing the ports to be configured in the container. | +| - [volumes](#containers_additionalProperties_allOf_i0_volumes ) | No | object | No | - | A dict containing the volumes to be configured in the container. | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > image` + +Configures the container image. + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| --------------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | -------------------------------------- | +| + [repository](#containers_additionalProperties_allOf_i0_image_repository ) | No | string | No | - | The repository of the container image. | +| + [tag](#containers_additionalProperties_allOf_i0_image_tag ) | No | string | No | - | The tag of the container image. | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > image > repository` + +The repository of the container image. + +##### `vyos_container > containers > Container entry > allOf > Base fields > image > tag` + +The tag of the container image. + +##### `vyos_container > containers > Container entry > allOf > Base fields > cap-add` + +A list of capabilities to be added to the container. + +**Example:** + +```yaml +cap-add: +- net-admin + +``` + +| Each item of this array must be | Description | +| ---------------------------------------------------------------- | ----------- | +| [items](#containers_additionalProperties_allOf_i0_cap-add_items) | - | +| | | + +##### vyos_container > containers > Container entry > allOf > Base fields > cap-add > items + +##### `vyos_container > containers > Container entry > allOf > Base fields > env` + +A dict containing the environment variables to be set in the container. + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| --------------------------------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ----------------- | +| - [additionalProperties](#containers_additionalProperties_allOf_i0_env_additionalProperties ) | No | string | No | - | - | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > env > additionalProperties` + +##### `vyos_container > containers > Container entry > allOf > Base fields > ports` + +A dict containing the ports to be configured in the container. + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ----------------------------------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ----------------- | +| - [additionalProperties](#containers_additionalProperties_allOf_i0_ports_additionalProperties ) | No | object | No | - | - | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > ports > additionalProperties` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------------------------------------------------------------------- | ------- | ------- | ---------- | ---------- | -------------------------------------- | +| + [source](#containers_additionalProperties_allOf_i0_ports_additionalProperties_source ) | No | integer | No | - | The source port on the host. | +| + [destination](#containers_additionalProperties_allOf_i0_ports_additionalProperties_destination ) | No | integer | No | - | The destination port on the container. | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > ports > additionalProperties > source` + +The source port on the host. + +##### `vyos_container > containers > Container entry > allOf > Base fields > ports > additionalProperties > destination` + +The destination port on the container. + +##### `vyos_container > containers > Container entry > allOf > Base fields > volumes` + +A dict containing the volumes to be configured in the container. + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ------------------------------------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ----------------- | +| - [additionalProperties](#containers_additionalProperties_allOf_i0_volumes_additionalProperties ) | No | object | No | - | - | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > volumes > additionalProperties` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ---------------------------------------------------------------------------------------------------- | ------- | ------- | ---------- | ---------- | -------------------------------------- | +| + [source](#containers_additionalProperties_allOf_i0_volumes_additionalProperties_source ) | No | integer | No | - | The source port on the host. | +| + [destination](#containers_additionalProperties_allOf_i0_volumes_additionalProperties_destination ) | No | integer | No | - | The destination port on the container. | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Base fields > volumes > additionalProperties > source` + +The source port on the host. + +##### `vyos_container > containers > Container entry > allOf > Base fields > volumes > additionalProperties > destination` + +The destination port on the container. + +#### `vyos_container > containers > Container entry > allOf > Networking fields` + +**Title:** Networking fields + +| One of(Option) | +| -------------------------------------------------------------------------- | +| [Host networking](#containers_additionalProperties_allOf_i1_oneOf_i0) | +| [Container networking](#containers_additionalProperties_allOf_i1_oneOf_i1) | +| | + +##### `vyos_container > containers > Container entry > allOf > Networking fields > oneOf > Host networking` + +**Title:** Host networking + +**Example:** + +```yaml +allow-host-networks: true + +``` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ------------------------------------------------------------------------------------------------ | ------- | ------- | ---------- | ---------- | ----------------------------------------------- | +| + [allow-host-networks](#containers_additionalProperties_allOf_i1_oneOf_i0_allow-host-networks ) | No | boolean | No | - | Run the container with host networking enabled. | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Networking fields > oneOf > Host networking > allow-host-networks` + +Run the container with host networking enabled. + +##### `vyos_container > containers > Container entry > allOf > Networking fields > oneOf > Container networking` + +**Title:** Container networking + +Use this if you want to configure this container with container networking. + +**Example:** + +```yaml +networks: + services: + ipv4_hostid: 10 + +``` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | -------------------------------------------------------------- | +| + [networks](#containers_additionalProperties_allOf_i1_oneOf_i1_networks ) | No | object | No | - | Configures the container networks the container should run on. | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Networking fields > oneOf > Container networking > networks` + +Configures the container networks the container should run on. + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ----------------------------------------------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ----------------------- | +| - [additionalProperties](#containers_additionalProperties_allOf_i1_oneOf_i1_networks_additionalProperties ) | No | object | No | - | Container network entry | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Networking fields > oneOf > Container networking > networks > Container network entry` + +**Title:** Container network entry + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------------------------------------------------------------------------------- | ------- | ------- | ---------- | ---------- | ---------------------------------------------------------------------------- | +| - [ipv4_hostid](#containers_additionalProperties_allOf_i1_oneOf_i1_networks_additionalProperties_ipv4_hostid ) | No | integer | No | - | The host id that is used to generate the IP address on the specified network | +| | | | | | | + +##### `vyos_container > containers > Container entry > allOf > Networking fields > oneOf > Container networking > networks > Container network entry > ipv4_hostid` + +The host id that is used to generate the IP address on the specified network + +---------------------------------------------------------------------------------------------------------------------------- +Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2021-11-19 at 13:42:22 +0100 \ No newline at end of file diff --git a/docs/variables/vyos_coredns.schema.md b/docs/variables/vyos_coredns.schema.md new file mode 100644 index 0000000..389ec9c --- /dev/null +++ b/docs/variables/vyos_coredns.schema.md @@ -0,0 +1,155 @@ +# vyos_coredns + +**Title:** vyos_coredns + +VyOS CoreDNS configuration + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ------------------------------ | ------- | ------- | ---------- | ---------- | ---------------------------------------------------------- | +| - [enabled](#enabled ) | No | boolean | No | - | Enable a CoreDNS container. | +| - [config_path](#config_path ) | No | string | No | - | Path on the VyOS router where the configuration is stored. | +| - [container](#container ) | No | object | No | - | CoreDNS container configuration | +| - [k8s_gateway](#k8s_gateway ) | No | object | No | - | CoreDNS k8s_gateway plugin configuration | +| | | | | | | + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > enabled` + +Enable a CoreDNS container. + +**Example:** + +```yaml +enabled: true + +``` + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > config_path` + +Path on the VyOS router where the configuration is stored. + +**Example:** + +```yaml +config_path: /config/coredns + +``` + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > container` + +CoreDNS container configuration + +**Example:** + +```yaml +container: + repository: ghcr.io/k8s-at-home/coredns + tag: v1.8.4 + +``` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------- | ------- | ------ | ---------- | ---------- | ------------------------------------------------------------------------------------ | +| + [repository](#container_repository ) | No | string | No | - | Configure the CoreDNS image repository. | +| + [tag](#container_tag ) | No | string | No | - | Configure the CoreDNS image tag. | +| - [name](#container_name ) | No | string | No | - | Configure the CoreDNS container name. Defaults to 'vyos-coredns' | +| - [networks](#container_networks ) | No | object | No | - | Configure CoreDNS container networking. If not present, the container will be co ... | +| | | | | | | + +### ![badge](https://img.shields.io/badge/Required-blue) `vyos_coredns > container > repository` + +Configure the CoreDNS image repository. + +### ![badge](https://img.shields.io/badge/Required-blue) `vyos_coredns > container > tag` + +Configure the CoreDNS image tag. + +### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > container > name` + +Configure the CoreDNS container name. Defaults to `vyos-coredns` + +### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > container > networks` + +Configure CoreDNS container networking. If not present, the container will be configured with host networking. + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ------------------------------------------------------------------- | ------- | ------ | ---------- | ---------- | ----------------------- | +| - [additionalProperties](#container_networks_additionalProperties ) | No | object | No | - | Container network entry | +| | | | | | | + +#### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > container > networks > Container network entry` + +**Title:** Container network entry + +**Example:** + +```yaml +networks: + services: + ipv4_hostid: 2 + +``` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| ---------------------------------------------------------------------- | ------- | ------- | ---------- | ---------- | ---------------------------------------------------------------------------- | +| - [ipv4_hostid](#container_networks_additionalProperties_ipv4_hostid ) | No | integer | No | - | The host id that is used to generate the IP address on the specified network | +| | | | | | | + +##### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > container > networks > Container network entry > ipv4_hostid` + +The host id that is used to generate the IP address on the specified network + +## ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > k8s_gateway` + +CoreDNS k8s_gateway plugin configuration + +**Example:** + +```yaml +k8s_gateway: + api_ip: 10.0.0.10 + domains: + - example.com + enabled: true + service_account: coredns + service_account_ns: default + +``` + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------------------------- | ------- | --------------- | ---------- | ---------- | ------------------------------------------------------------------------------------ | +| - [enabled](#k8s_gateway_enabled ) | No | boolean | No | - | Enable the k8s_gateway plugin. | +| + [api_ip](#k8s_gateway_api_ip ) | No | string | No | - | Address where the K8s API can be reached. | +| + [domains](#k8s_gateway_domains ) | No | array of string | No | - | Configure the domains which should be handled by the k8s_gatway plugin. | +| + [service_account_ns](#k8s_gateway_service_account_ns ) | No | string | No | - | Namespace where the ServiceAccount is defined. | +| + [service_account](#k8s_gateway_service_account ) | No | string | No | - | Name of the ServiceAccount that is used to communicate with the K8s API. This ro ... | +| | | | | | | + +### ![badge](https://img.shields.io/badge/Optional-yellow) `vyos_coredns > k8s_gateway > enabled` + +Enable the k8s_gateway plugin. + +### ![badge](https://img.shields.io/badge/Required-blue) `vyos_coredns > k8s_gateway > api_ip` + +Address where the K8s API can be reached. + +### ![badge](https://img.shields.io/badge/Required-blue) `vyos_coredns > k8s_gateway > domains` + +Configure the domains which should be handled by the k8s_gatway plugin. + +| Each item of this array must be | Description | +| ----------------------------------- | ----------- | +| [items](#k8s_gateway_domains_items) | - | +| | | + +#### vyos_coredns > k8s_gateway > domains > items + +### ![badge](https://img.shields.io/badge/Required-blue) `vyos_coredns > k8s_gateway > service_account_ns` + +Namespace where the ServiceAccount is defined. + +### ![badge](https://img.shields.io/badge/Required-blue) `vyos_coredns > k8s_gateway > service_account` + +Name of the ServiceAccount that is used to communicate with the K8s API. This role does *not* create the ServiceAccount. + +---------------------------------------------------------------------------------------------------------------------------- +Generated using [json-schema-for-humans](https://github.com/coveooss/json-schema-for-humans) on 2021-11-19 at 13:42:22 +0100 \ No newline at end of file diff --git a/mkdocs.yaml b/mkdocs.yaml index 65db95c..25a5782 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -42,6 +42,7 @@ theme: # Plugins plugins: + - awesome-pages - search: lang: en - minify: @@ -54,14 +55,9 @@ markdown_extensions: - attr_list - toc: permalink: true + toc_depth: "1-2" # Customization -extra_javascript: - - _assets/js/fancybox.umd.js - -extra_css: - - _assets/css/fancybox.css - extra: social: - icon: fontawesome/brands/github @@ -71,3 +67,4 @@ extra: nav: - Home: - "index.md" + - ... diff --git a/requirements-mkdocs.txt b/requirements-mkdocs.txt index cfcfaef..edab861 100644 --- a/requirements-mkdocs.txt +++ b/requirements-mkdocs.txt @@ -1,4 +1,5 @@ mkdocs~=1.2.0 +mkdocs-awesome-pages-plugin~=2.6.0 mkdocs-macros-plugin~=0.6.0 mkdocs-material~=7.3.0 mkdocs-minify-plugin~=0.4.0 diff --git a/tasks/containers/containers.yml b/tasks/containers/containers.yml new file mode 100644 index 0000000..aaafc7f --- /dev/null +++ b/tasks/containers/containers.yml @@ -0,0 +1,14 @@ +--- +- name: containers | containers | read config json + ansible.builtin.include_tasks: + file: "{{ role_path }}/tasks/_utils/get_config_json.yml" + apply: + vars: + source: "containers | containers" + +- name: containers | containers | define entries + vars: + current_config: "{{ vyos_active_config_json['container']['name'] | default({}) }}" + vyos.vyos.vyos_config: + match: line + lines: "{{ lookup('template', './config/containers/containers.j2') }}" diff --git a/tasks/containers/main.yml b/tasks/containers/main.yml new file mode 100644 index 0000000..96021c4 --- /dev/null +++ b/tasks/containers/main.yml @@ -0,0 +1,18 @@ +--- +- name: containers | registries + ansible.builtin.import_tasks: + file: registries.yml + tags: + - vyos_containers_registries + +- name: containers | networks + ansible.builtin.import_tasks: + file: networks.yml + tags: + - vyos_containers_networks + +- name: containers | containers + ansible.builtin.import_tasks: + file: containers.yml + tags: + - vyos_containers_containers diff --git a/tasks/containers/networks.yml b/tasks/containers/networks.yml new file mode 100644 index 0000000..20a4feb --- /dev/null +++ b/tasks/containers/networks.yml @@ -0,0 +1,14 @@ +--- +- name: containers | networks | read config json + ansible.builtin.include_tasks: + file: "{{ role_path }}/tasks/_utils/get_config_json.yml" + apply: + vars: + source: "containers | networks" + +- name: containers | networks | define entries + vars: + current_config: "{{ vyos_active_config_json['container']['network'] | default({}) }}" + vyos.vyos.vyos_config: + match: line + lines: "{{ lookup('template', './config/containers/networks.j2') }}" diff --git a/tasks/containers/registries.yml b/tasks/containers/registries.yml new file mode 100644 index 0000000..8438764 --- /dev/null +++ b/tasks/containers/registries.yml @@ -0,0 +1,14 @@ +--- +- name: containers | registries | read config json + ansible.builtin.include_tasks: + file: "{{ role_path }}/tasks/_utils/get_config_json.yml" + apply: + vars: + source: "containers | registries" + +- name: containers | registries | define entries + vars: + current_config: "{{ vyos_active_config_json['container']['registry'] | default({}) }}" + vyos.vyos.vyos_config: + match: line + lines: "{{ lookup('template', './config/containers/registries.j2') }}" diff --git a/tasks/dns/coredns.yml b/tasks/dns/coredns.yml index 9a4715e..d861395 100644 --- a/tasks/dns/coredns.yml +++ b/tasks/dns/coredns.yml @@ -47,3 +47,69 @@ - name: dns | coredns | switch to network_cli connection set_fact: ansible_connection: ansible.netcommon.network_cli + + +- name: dns | coredns | add base container + vars: + container_name: "{{ vyos_coredns['container']['name'] | default('vyos-coredns') }}" + ansible.builtin.set_fact: + vyos_containers: | + {{ + vyos_containers | combine ({ + 'containers': { + container_name: { + 'image': { + 'repository': vyos_coredns['container']['repository'], + 'tag': vyos_coredns['container']['tag'] + }, + 'volumes': { + 'config': { + 'source': vyos_coredns['config_path'], + 'destination': '/config' + }, + 'hostsfile': { + 'source': '/etc/hosts', + 'destination': '/etc/hosts:ro' + } + }, + 'cap-add': [ + 'net-bind-service' + ] + } + } + }, recursive=true) + }} + +- name: dns | coredns | add container host networking + when: + - vyos_coredns['container']['networks'] is undefined + vars: + container_name: "{{ vyos_coredns['container']['name'] | default('vyos-coredns') }}" + ansible.builtin.set_fact: + vyos_containers: | + {{ + vyos_containers | combine ({ + 'containers': { + container_name: { + 'allow-host-networks': true + } + } + }, recursive=true) + }} + +- name: dns | coredns | add container networking + when: + - vyos_coredns['container']['networks'] is defined + vars: + container_name: "{{ vyos_coredns['container']['name'] | default('vyos-coredns') }}" + ansible.builtin.set_fact: + vyos_containers: | + {{ + vyos_containers | combine ({ + 'containers': { + container_name: { + 'networks': (vyos_coredns['container']['networks'] | from_yaml) + } + } + }, recursive=true) + }} diff --git a/tasks/main.yml b/tasks/main.yml index 3761d49..1736e84 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -53,6 +53,12 @@ tags: - vyos_dns +- name: containers + ansible.builtin.import_tasks: + file: containers/main.yml + tags: + - vyos_containers + - name: post-config ansible.builtin.import_tasks: file: post_config/main.yml diff --git a/tasks/post_config/main.yml b/tasks/post_config/main.yml index cb2b190..260b7ba 100644 --- a/tasks/post_config/main.yml +++ b/tasks/post_config/main.yml @@ -1,11 +1,4 @@ --- - -- name: post-config | manage script - ansible.builtin.import_tasks: - file: manage_script.yml - tags: - - vyos_post_config_script - - name: post-config | save configuration when: - vyos_save_config diff --git a/tasks/post_config/manage_script.yml b/tasks/post_config/manage_script.yml deleted file mode 100644 index 6c0d01d..0000000 --- a/tasks/post_config/manage_script.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -- name: post-config | manage script | switch to ssh connection - set_fact: - ansible_connection: ssh - -- name: post-config | manage script | copy script - ansible.builtin.template: - src: config/post_config/vyos-postconfig-bootup.script.j2 - dest: /config/scripts/vyos-postconfig-bootup.script - owner: root - group: vyattacfg - mode: "0775" - lstrip_blocks: true - become: true - -- name: post-config | manage script | run - when: - vyos_run_post_config_script - ansible.builtin.command: - cmd: /config/scripts/vyos-postconfig-bootup.script - become: true - -- name: post-config | manage script | switch to network_cli connection - set_fact: - ansible_connection: ansible.netcommon.network_cli diff --git a/templates/config/containers/containers.j2 b/templates/config/containers/containers.j2 new file mode 100644 index 0000000..e1ccf36 --- /dev/null +++ b/templates/config/containers/containers.j2 @@ -0,0 +1,87 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" +{%- import "macros/networks.j2" as networks with context -%} +{%- from "macros/config.j2" import generate_config -%} + +{%- set containers = dict() -%} + +{#- Containers from gateway config -#} +{%- set config_containers = (vyos_containers['containers'] | default(dict())) -%} +{%- for container_name, container_config in config_containers.items() %} + {%- set entry = dict() -%} + + {%- set _ = entry.update({'image': container_config['image']['repository'] + ':' + container_config['image']['tag'] }) -%} + + {%- if container_config['allow-host-networks'] is defined and container_config['allow-host-networks'] -%} + {%- set _ = entry.update({'allow-host-networks': {}}) -%} + {%- endif -%} + + {%- if container_config['cap-add'] is defined -%} + {%- set capabilities = dict() -%} + {%- for cap in container_config['cap-add'] %} + {%- set _ = capabilities.update({cap: {}}) -%} + {%- endfor -%} + {%- set _ = entry.update({'cap-add': capabilities}) -%} + {%- endif -%} + + {%- if container_config['env'] is defined -%} + {%- set env_item = dict() -%} + {%- for env_key, env_value in container_config['env'].items() %} + {%- set _ = env_item.update({ + env_key: { + 'value': env_value + } + }) -%} + {%- endfor -%} + {%- set _ = entry.update({'environment': env_item}) -%} + {%- endif -%} + + {%- if container_config['volumes'] is defined -%} + {%- set volumes = dict() -%} + {%- for vol_name, vol_config in container_config['volumes'].items() %} + {%- set _ = volumes.update({vol_name: vol_config}) -%} + {%- endfor -%} + {%- set _ = entry.update({'volume': volumes}) -%} + {%- endif -%} + + {%- if container_config['ports'] is defined -%} + {%- set ports = dict() -%} + {%- for port_name, port_config in container_config['ports'].items() %} + {%- set _ = ports.update({port_name: port_config}) -%} + {%- endfor -%} + {%- set _ = entry.update({'port': ports}) -%} + {%- endif -%} + + {%- if container_config['networks'] is defined -%} + {%- set container_network_entry = dict() -%} + {%- for container_network_name, container_network_config in container_config['networks'].items() %} + + {#- Get the network config from the gateway config -#} + {%- set container_network = vyos_containers['networks'][container_network_name] -%} + {#- Determine the configured network CIDR -#} + {%- if container_network['network'] is defined -%} + {%- set network = networks.get_network_by_name(container_network['network']) | from_yaml -%} + {%- set cidr = network['cidr'] -%} + {%- elif container_network['prefix'] is defined -%} + {%- set cidr = container_network['prefix'] -%} + {%- endif -%} + + {#- Determine the IP address and add it to the container -#} + {%- set _ = container_network_entry.update({ + container_network_name: { + 'address': (cidr | ansible.netcommon.ipv4(container_network_config['ipv4_hostid']) | ansible.netcommon.ipv4('address')) + } + }) -%} + {%- endfor -%} + {%- set _ = entry.update({'network': container_network_entry}) -%} + {%- endif -%} + + {%- set _ = containers.update({container_name: entry}) -%} +{%- endfor -%} + +{#- Process the collected config entries -#} +{%- if containers | length > 0 -%} + {{- generate_config(current_config, containers, ['container', 'name']) -}} +{%- else -%} + {#- Remove the entire key if no config is found -#} + delete container name +{%- endif -%} diff --git a/templates/config/containers/networks.j2 b/templates/config/containers/networks.j2 new file mode 100644 index 0000000..8b457bd --- /dev/null +++ b/templates/config/containers/networks.j2 @@ -0,0 +1,31 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" +{%- from "macros/config.j2" import generate_config -%} +{%- import "macros/networks.j2" as networks with context -%} + +{%- set container_networks = dict() -%} + +{#- Networks from gateway config -#} +{%- set config_container_networks = (vyos_containers['networks'] | default(dict())) -%} +{%- for network_name, network_config in config_container_networks.items() %} + {%- set entry = dict() -%} + {%- if network_config['description'] is defined -%} + {%- set _ = entry.update({'description': network_config['description']}) -%} + {%- endif -%} + + {%- if network_config['network'] is defined -%} + {%- set network = networks.get_network_by_name(network_config['network']) | from_yaml -%} + {%- set _ = entry.update({'prefix': network['cidr']}) -%} + {%- elif network_config['prefix'] is defined -%} + {%- set _ = entry.update({'prefix': network_config['prefix']}) -%} + {%- endif -%} + + {%- set _ = container_networks.update({network_name: entry}) -%} +{%- endfor -%} + +{#- Process the collected config entries -#} +{%- if container_networks | length > 0 -%} + {{- generate_config(current_config, container_networks, ['container', 'network']) -}} +{%- else -%} + {#- Remove the entire key if no config is found -#} + delete container network +{%- endif -%} diff --git a/templates/config/containers/registries.j2 b/templates/config/containers/registries.j2 new file mode 100644 index 0000000..c7f0983 --- /dev/null +++ b/templates/config/containers/registries.j2 @@ -0,0 +1,17 @@ +#jinja2: lstrip_blocks: "True", trim_blocks: "True" +{%- from "macros/config.j2" import generate_config -%} + +{%- set registries = dict() -%} + +{#- Registries from gateway config -#} +{%- for registry in (vyos_containers['registries'] | default(dict())) %} + {%- set _ = registries.update({registry: {}}) -%} +{%- endfor -%} + +{#- Process the collected config entries -#} +{%- if registries | length > 0 -%} + {{- generate_config(current_config, registries, ['container', 'registry']) -}} +{%- else -%} + {#- Remove the entire key if no config is found -#} + delete container registry +{%- endif -%} diff --git a/templates/config/post_config/vyos-postconfig-bootup.script.j2 b/templates/config/post_config/vyos-postconfig-bootup.script.j2 deleted file mode 100644 index 526b778..0000000 --- a/templates/config/post_config/vyos-postconfig-bootup.script.j2 +++ /dev/null @@ -1,32 +0,0 @@ -#jinja2: lstrip_blocks: "True", trim_blocks: "True" -{%- from "macros/container.j2" import container with context -%} -#!/bin/sh -# This script is executed at boot time after VyOS configuration is fully applied. -# Any modifications required to work around unfixed bugs -# or use services not available through the VyOS CLI system can be placed here. - -{% if vyos_coredns['enabled'] %} -# CoreDNS workaround until podman is fully supported on CLI -{{ container( - 'coredns', - { - 'image': { - 'repository': vyos_coredns['container']['repository'], - 'tag': vyos_coredns['container']['tag'] - }, - 'volumes': [ - vyos_coredns['config_path'] + ':/config', - '/etc/hosts:/etc/hosts:ro' - ], - 'cap_add': [ - 'CAP_NET_BIND_SERVICE' - ], - 'host-networking': true - } -) }} -{% endif %} - -{% for container_name, container_config in vyos_containers.items() %} -{{ container(container_name, container_config) }} - -{% endfor -%} diff --git a/templates/macros/container.j2 b/templates/macros/container.j2 deleted file mode 100644 index 19ba4af..0000000 --- a/templates/macros/container.j2 +++ /dev/null @@ -1,47 +0,0 @@ -{# Template that generates container systemd units #} -{%- macro container(name, config) -%} - {%- set run_args = [] -%} - - {%- for volume in (config['volumes'] | default([])) -%} - {%- set _ = run_args.append('-v') -%} - {%- set _ = run_args.append(volume) -%} - {%- endfor -%} - - {%- for cap in (config['cap_add'] | default([])) -%} - {%- set _ = run_args.append('--cap-add') -%} - {%- set _ = run_args.append(cap) -%} - {%- endfor -%} - - {%- if config['env'] is defined -%} - {%- for key, value in config['env'].items() -%} - {%- set _ = run_args.append('--env') -%} - {%- set _ = run_args.append(key + '=' + value) -%} - {%- endfor -%} - {%- endif -%} - - {%- if (config['host-networking'] | default(false)) %} - {%- set _ = run_args.append('--net') -%} - {%- set _ = run_args.append('host') -%} - {%- endif -%} -cat < /etc/systemd/system/{{ name }}.service -[Unit] -Description={{ name }} container -Wants=network.target -After=network-online.target - -[Service] -Restart=on-failure -TimeoutStopSec=60 -ExecStartPre=/usr/bin/podman rm --force --volumes --ignore {{ name }} -ExecStart=/usr/bin/podman run -d --rm --name {{ name }} {{ run_args | join(' ') }} {{ config['image']['repository'] }}:{{ config['image']['tag'] }} -ExecStop=/usr/bin/podman stop -t 10 {{ name }} -ExecStopPost=/usr/bin/podman stop -t 10 {{ name }} -Type=forking - -[Install] -WantedBy=multi-user.target default.target -EOT - -systemctl daemon-reload -systemctl start {{ name }} -{%- endmacro %}