From 23dedc6de3b2dfe9e8ea4108ea384b1b57094cb1 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Sun, 14 May 2023 15:31:58 +0300
Subject: [PATCH 001/138] Fix GH link in docs
---
docs/assets/docsify-init.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/assets/docsify-init.js b/docs/assets/docsify-init.js
index 08a0a968c5..3b01c4e4c2 100644
--- a/docs/assets/docsify-init.js
+++ b/docs/assets/docsify-init.js
@@ -10,7 +10,7 @@ const documentTitleBase = document.title;
const linksMenu = '';
From bc5ca9a344ee1c04d19664e51fcbc4453a48e329 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Mon, 15 May 2023 20:06:46 +0300
Subject: [PATCH 002/138] Add URL replacements
---
CHANGELOG.md | 2 ++
config/config.go | 7 ++++++-
config/configurators/configurators.go | 22 +++++++++++++++++++-
docs/configuration.md | 11 +++++++++-
options/processing_options_test.go | 29 +++++++++++++++++++++++++++
options/url.go | 10 ++++++---
6 files changed, 75 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index daa0c20fef..d3bbc1957c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+### Add
+- Add `IMGPROXY_URL_REPLACEMENTS` config.
## [3.17.0] - 2023-05-10
### Add
diff --git a/config/config.go b/config/config.go
index b5438bcec4..b60c81e378 100644
--- a/config/config.go
+++ b/config/config.go
@@ -126,7 +126,8 @@ var (
LastModifiedEnabled bool
- BaseURL string
+ BaseURL string
+ URLReplacements map[*regexp.Regexp]string
Presets []string
OnlyPresets bool
@@ -317,6 +318,7 @@ func Reset() {
LastModifiedEnabled = false
BaseURL = ""
+ URLReplacements = make(map[*regexp.Regexp]string)
Presets = make([]string, 0)
OnlyPresets = false
@@ -518,6 +520,9 @@ func Configure() error {
configurators.Bool(&LastModifiedEnabled, "IMGPROXY_USE_LAST_MODIFIED")
configurators.String(&BaseURL, "IMGPROXY_BASE_URL")
+ if err := configurators.Replacements(&URLReplacements, "IMGPROXY_URL_REPLACEMENTS"); err != nil {
+ return err
+ }
configurators.StringSlice(&Presets, "IMGPROXY_PRESETS")
if err := configurators.StringSliceFile(&Presets, presetsPath); err != nil {
diff --git a/config/configurators/configurators.go b/config/configurators/configurators.go
index 0079b6a4fe..945d21da6f 100644
--- a/config/configurators/configurators.go
+++ b/config/configurators/configurators.go
@@ -221,6 +221,26 @@ func Patterns(s *[]*regexp.Regexp, name string) {
}
}
+func Replacements(m *map[*regexp.Regexp]string, name string) error {
+ var sm map[string]string
+
+ if err := StringMap(&sm, name); err != nil {
+ return err
+ }
+
+ if len(sm) > 0 {
+ mm := make(map[*regexp.Regexp]string)
+
+ for k, v := range sm {
+ mm[RegexpFromPattern(k)] = v
+ }
+
+ *m = mm
+ }
+
+ return nil
+}
+
func RegexpFromPattern(pattern string) *regexp.Regexp {
var result strings.Builder
// Perform prefix matching
@@ -228,7 +248,7 @@ func RegexpFromPattern(pattern string) *regexp.Regexp {
for i, part := range strings.Split(pattern, "*") {
// Add a regexp match all without slashes for each wildcard character
if i > 0 {
- result.WriteString("[^/]*")
+ result.WriteString("([^/]*)")
}
// Quote other parts of the pattern
diff --git a/docs/configuration.md b/docs/configuration.md
index 4b4ae67658..0d3bcb713f 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -406,6 +406,16 @@ imgproxy can process files from OpenStack Object Storage, but this feature is di
Check out the [Serving files from OpenStack Object Storage](serving_files_from_openstack_swift.md) guide to learn more.
+## Source image URLs
+
+* `IMGPROXY_BASE_URL`: a base URL prefix that will be added to each source image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. If the image URL already contains the prefix, it won't be added. Default: blank
+
+* `IMGPROXY_URL_REPLACEMENTS`: a list of `pattern=replacement` pairs, semicolon (`;`) divided. imgproxy will replace source URL prefixes matching the pattern with the corresponding replacement. Wildcards can be included in patterns with `*` to match all characters except `/`. `${N}` in replacement strings will be replaced with wildcard values, where `N` is the number of the wildcard. Examples:
+ * `mys3://=s3://my_bucket/images/` will replace `mys3://image01.jpg` with `s3://my_bucket/images/image01.jpg`
+ * `mys3://*/=s3://my_bucket/${1}/images` will replace `mys3://items/image01.jpg` with `s3://my_bucket/items/images/image01.jpg`
+
+**📝 Note:** Replacements defined in `IMGPROXY_URL_REPLACEMENTS` are applied before `IMGPROXY_BASE_URL` is added.
+
## Metrics
### New Relic :id=new-relic-metrics
@@ -527,7 +537,6 @@ imgproxy can send logs to syslog, but this feature is disabled by default. To en
## Miscellaneous
-* `IMGPROXY_BASE_URL`: a base URL prefix that will be added to each requested image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. If the image URL already contains the prefix, it won't be added. Default: blank
* `IMGPROXY_USE_LINEAR_COLORSPACE`: when `true`, imgproxy will process images in linear colorspace. This will slow down processing. Note that images won't be fully processed in linear colorspace while shrink-on-load is enabled (see below).
* `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEGs and WebP files. Allows processing the entire image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images.
* `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true`
diff --git a/options/processing_options_test.go b/options/processing_options_test.go
index 4727c04193..2a48fa5a2a 100644
--- a/options/processing_options_test.go
+++ b/options/processing_options_test.go
@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"net/url"
+ "regexp"
"testing"
"github.com/stretchr/testify/require"
@@ -54,6 +55,20 @@ func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithBase() {
require.Equal(s.T(), imagetype.PNG, po.Format)
}
+func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithReplacement() {
+ config.URLReplacements = map[*regexp.Regexp]string{
+ regexp.MustCompile("^test://([^/]*)/"): "http://images.dev/${1}/dolor/",
+ }
+
+ originURL := "test://lorem/ipsum.jpg?param=value"
+ path := fmt.Sprintf("/size:100:100/%s.png", base64.RawURLEncoding.EncodeToString([]byte(originURL)))
+ po, imageURL, err := ParsePath(path, make(http.Header))
+
+ require.Nil(s.T(), err)
+ require.Equal(s.T(), "http://images.dev/lorem/dolor/ipsum.jpg?param=value", imageURL)
+ require.Equal(s.T(), imagetype.PNG, po.Format)
+}
+
func (s *ProcessingOptionsTestSuite) TestParsePlainURL() {
originURL := "http://images.dev/lorem/ipsum.jpg"
path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
@@ -96,6 +111,20 @@ func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithBase() {
require.Equal(s.T(), imagetype.PNG, po.Format)
}
+func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithReplacement() {
+ config.URLReplacements = map[*regexp.Regexp]string{
+ regexp.MustCompile("^test://([^/]*)/"): "http://images.dev/${1}/dolor/",
+ }
+
+ originURL := "test://lorem/ipsum.jpg"
+ path := fmt.Sprintf("/size:100:100/plain/%s@png", originURL)
+ po, imageURL, err := ParsePath(path, make(http.Header))
+
+ require.Nil(s.T(), err)
+ require.Equal(s.T(), "http://images.dev/lorem/dolor/ipsum.jpg", imageURL)
+ require.Equal(s.T(), imagetype.PNG, po.Format)
+}
+
func (s *ProcessingOptionsTestSuite) TestParsePlainURLEscapedWithBase() {
config.BaseURL = "http://images.dev/"
diff --git a/options/url.go b/options/url.go
index 850b796736..b4e8e5cc95 100644
--- a/options/url.go
+++ b/options/url.go
@@ -12,7 +12,11 @@ import (
const urlTokenPlain = "plain"
-func addBaseURL(u string) string {
+func preprocessURL(u string) string {
+ for re, repl := range config.URLReplacements {
+ u = re.ReplaceAllString(u, repl)
+ }
+
if len(config.BaseURL) == 0 || strings.HasPrefix(u, config.BaseURL) {
return u
}
@@ -43,7 +47,7 @@ func decodeBase64URL(parts []string) (string, string, error) {
return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
}
- return addBaseURL(string(imageURL)), format, nil
+ return preprocessURL(string(imageURL)), format, nil
}
func decodePlainURL(parts []string) (string, string, error) {
@@ -69,7 +73,7 @@ func decodePlainURL(parts []string) (string, string, error) {
return "", "", fmt.Errorf("Invalid url encoding: %s", encoded)
}
- return addBaseURL(unescaped), format, nil
+ return preprocessURL(unescaped), format, nil
}
func DecodeURL(parts []string) (string, string, error) {
From 0ea06c88c2534c798ebc62e96fe86b825f23b973 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 17 May 2023 13:41:30 +0300
Subject: [PATCH 003/138] Update base Docker image
---
docker/Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index fb8d1c64c6..95aeca064f 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE_VERSION="v3.3.5"
+ARG BASE_IMAGE_VERSION="v3.4.0"
FROM darthsim/imgproxy-base:${BASE_IMAGE_VERSION}
From aad0186b0a28bb5c79242cc4e3ed43dc3166a648 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 17 May 2023 13:42:30 +0300
Subject: [PATCH 004/138] Enable vector optimizations in vips
---
vips/vips.go | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/vips/vips.go b/vips/vips.go
index 39949e336c..2dee4b683f 100644
--- a/vips/vips.go
+++ b/vips/vips.go
@@ -62,9 +62,7 @@ func Init() error {
C.vips_concurrency_set(1)
- // Vector calculations cause SIGSEGV sometimes when working with JPEG.
- // It's better to disable it since profit it quite small
- C.vips_vector_set_enabled(0)
+ C.vips_vector_set_enabled(1)
if len(os.Getenv("IMGPROXY_VIPS_LEAK_CHECK")) > 0 {
C.vips_leak_set(C.gboolean(1))
From aaaf1c706d755c5352b46fe0fd32691fa79740a7 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 17 May 2023 17:47:20 +0300
Subject: [PATCH 005/138] Normalize IMGPROXY_PATH_PREFIX and
IMGPROXY_HEALTH_CHECK_PATH
---
config/config.go | 4 ++--
config/configurators/configurators.go | 19 +++++++++++++++++++
healthcheck.go | 2 +-
3 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/config/config.go b/config/config.go
index b60c81e378..05ded60deb 100644
--- a/config/config.go
+++ b/config/config.go
@@ -406,7 +406,7 @@ func Configure() error {
configurators.Bool(&SoReuseport, "IMGPROXY_SO_REUSEPORT")
- configurators.String(&PathPrefix, "IMGPROXY_PATH_PREFIX")
+ configurators.URLPath(&PathPrefix, "IMGPROXY_PATH_PREFIX")
configurators.MegaInt(&MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
configurators.Int(&MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE")
@@ -449,7 +449,7 @@ func Configure() error {
configurators.Bool(&EnforceAvif, "IMGPROXY_ENFORCE_AVIF")
configurators.Bool(&EnableClientHints, "IMGPROXY_ENABLE_CLIENT_HINTS")
- configurators.String(&HealthCheckPath, "IMGPROXY_HEALTH_CHECK_PATH")
+ configurators.URLPath(&HealthCheckPath, "IMGPROXY_HEALTH_CHECK_PATH")
if err := configurators.ImageTypes(&PreferredFormats, "IMGPROXY_PREFERRED_FORMATS"); err != nil {
return err
diff --git a/config/configurators/configurators.go b/config/configurators/configurators.go
index 945d21da6f..de3052a1d2 100644
--- a/config/configurators/configurators.go
+++ b/config/configurators/configurators.go
@@ -102,6 +102,25 @@ func Bool(b *bool, name string) {
}
}
+func URLPath(s *string, name string) {
+ if env := os.Getenv(name); len(env) > 0 {
+ if i := strings.IndexByte(env, '?'); i >= 0 {
+ env = env[:i]
+ }
+ if i := strings.IndexByte(env, '#'); i >= 0 {
+ env = env[:i]
+ }
+ if len(env) > 0 && env[len(env)-1] == '/' {
+ env = env[:len(env)-1]
+ }
+ if len(env) > 0 && env[0] != '/' {
+ env = "/" + env
+ }
+
+ *s = env
+ }
+}
+
func ImageTypes(it *[]imagetype.Type, name string) error {
if env := os.Getenv(name); len(env) > 0 {
parts := strings.Split(env, ",")
diff --git a/healthcheck.go b/healthcheck.go
index df69fd4acb..9843795e11 100644
--- a/healthcheck.go
+++ b/healthcheck.go
@@ -19,7 +19,7 @@ func healthcheck() int {
configurators.String(&network, "IMGPROXY_NETWORK")
configurators.String(&bind, "IMGPROXY_BIND")
- configurators.String(&pathprefix, "IMGPROXY_PATH_PREFIX")
+ configurators.URLPath(&pathprefix, "IMGPROXY_PATH_PREFIX")
httpc := http.Client{
Transport: &http.Transport{
From 219d37ee5ba1b4fb447ecb86e68694f6f0df1180 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 17 May 2023 20:25:45 +0300
Subject: [PATCH 006/138] Update base Docker image
---
docker/Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 95aeca064f..60b9d26076 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE_VERSION="v3.4.0"
+ARG BASE_IMAGE_VERSION="v3.5.0"
FROM darthsim/imgproxy-base:${BASE_IMAGE_VERSION}
From ce24671ca0659fe8dd16b02615f2d9089e74b7f3 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 17 May 2023 20:26:20 +0300
Subject: [PATCH 007/138] Update NOTICE
---
NOTICE | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/NOTICE b/NOTICE
index 5f334ac22c..4bc6a2ebfb 100644
--- a/NOTICE
+++ b/NOTICE
@@ -5101,6 +5101,121 @@ zlib
================================================================================
+zlib-ng
+
+ (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+================================================================================
+
+libffi
+
+ libffi - Copyright (c) 1996-2022 Anthony Green, Red Hat, Inc and others.
+ See source files for details.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ ``Software''), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+================================================================================
+
+Orc
+
+ The majority of the source code and the collective work is subject
+ to the following license:
+
+ Copyright 2002 - 2009 David A. Schleef
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+ The source code implementing the Mersenne Twister algorithm is
+ subject to the following license:
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+================================================================================
+
libtiff
Acknowledgments and Other Issues
From 3048c30e1e1d14d471795837335d9f9d2171c5a1 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 23 May 2023 18:26:06 +0300
Subject: [PATCH 008/138] Cast to origin pixel format after premultiplication
---
vips/vips.c | 46 +++++++++++++++++++++++++---------------------
1 file changed, 25 insertions(+), 21 deletions(-)
diff --git a/vips/vips.c b/vips/vips.c
index a56eea6eb9..4178ff29b2 100644
--- a/vips/vips.c
+++ b/vips/vips.c
@@ -200,13 +200,14 @@ vips_resize_go(VipsImage *in, VipsImage **out, double wscale, double hscale) {
VipsBandFormat format = vips_band_format(in);
VipsImage *base = vips_image_new();
- VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 3);
+ VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 4);
int res =
vips_premultiply(in, &t[0], NULL) ||
- vips_resize(t[0], &t[1], wscale, "vscale", hscale, NULL) ||
- vips_unpremultiply(t[1], &t[2], NULL) ||
- vips_cast(t[2], out, format, NULL);
+ vips_cast(t[0], &t[1], format, NULL) ||
+ vips_resize(t[1], &t[2], wscale, "vscale", hscale, NULL) ||
+ vips_unpremultiply(t[2], &t[3], NULL) ||
+ vips_cast(t[3], out, format, NULL);
clear_image(&base);
@@ -306,14 +307,17 @@ vips_apply_filters(VipsImage *in, VipsImage **out, double blur_sigma,
double sharp_sigma, int pixelate_pixels) {
VipsImage *base = vips_image_new();
- VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 9);
+ VipsImage **t = (VipsImage **) vips_object_local_array(VIPS_OBJECT(base), 10);
VipsInterpretation interpretation = in->Type;
VipsBandFormat format = in->BandFmt;
gboolean premultiplied = FALSE;
if ((blur_sigma > 0 || sharp_sigma > 0) && vips_image_hasalpha(in)) {
- if (vips_premultiply(in, &t[0], NULL)) {
+ if (
+ vips_premultiply(in, &t[0], NULL) ||
+ vips_cast(t[0], &t[1], format, NULL)
+ ) {
clear_image(&base);
return 1;
}
@@ -323,21 +327,21 @@ vips_apply_filters(VipsImage *in, VipsImage **out, double blur_sigma,
}
if (blur_sigma > 0.0) {
- if (vips_gaussblur(in, &t[1], blur_sigma, NULL)) {
+ if (vips_gaussblur(in, &t[2], blur_sigma, NULL)) {
clear_image(&base);
return 1;
}
- in = t[1];
+ in = t[2];
}
if (sharp_sigma > 0.0) {
- if (vips_sharpen(in, &t[2], "sigma", sharp_sigma, NULL)) {
+ if (vips_sharpen(in, &t[3], "sigma", sharp_sigma, NULL)) {
clear_image(&base);
return 1;
}
- in = t[2];
+ in = t[3];
}
pixelate_pixels = VIPS_MIN(pixelate_pixels, VIPS_MAX(in->Xsize, in->Ysize));
@@ -352,46 +356,46 @@ vips_apply_filters(VipsImage *in, VipsImage **out, double blur_sigma,
th = (int)(VIPS_CEIL((double)h / pixelate_pixels)) * pixelate_pixels;
if (tw > w || th > h) {
- if (vips_embed(in, &t[3], 0, 0, tw, th, "extend", VIPS_EXTEND_MIRROR, NULL)) {
+ if (vips_embed(in, &t[4], 0, 0, tw, th, "extend", VIPS_EXTEND_MIRROR, NULL)) {
clear_image(&base);
return 1;
}
- in = t[3];
+ in = t[4];
}
if (
- vips_shrink(in, &t[4], pixelate_pixels, pixelate_pixels, NULL) ||
- vips_zoom(t[4], &t[5], pixelate_pixels, pixelate_pixels, NULL)
+ vips_shrink(in, &t[5], pixelate_pixels, pixelate_pixels, NULL) ||
+ vips_zoom(t[5], &t[6], pixelate_pixels, pixelate_pixels, NULL)
) {
clear_image(&base);
return 1;
}
- in = t[5];
+ in = t[6];
if (tw > w || th > h) {
- if (vips_extract_area(in, &t[6], 0, 0, w, h, NULL)) {
+ if (vips_extract_area(in, &t[7], 0, 0, w, h, NULL)) {
clear_image(&base);
return 1;
}
- in = t[6];
+ in = t[7];
}
}
if (premultiplied) {
- if (vips_unpremultiply(in, &t[7], NULL)) {
+ if (vips_unpremultiply(in, &t[8], NULL)) {
clear_image(&base);
return 1;
}
- in = t[7];
+ in = t[8];
}
int res =
- vips_colourspace(in, &t[8], interpretation, NULL) ||
- vips_cast(t[8], out, format, NULL);
+ vips_colourspace(in, &t[9], interpretation, NULL) ||
+ vips_cast(t[9], out, format, NULL);
clear_image(&base);
From 157843ccb32ac4c8d365e57c2081bdde278a2b15 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 23 May 2023 19:08:35 +0300
Subject: [PATCH 009/138] Use VIPS_META_BITS_PER_SAMPLE image header instead of
palette-bit-depth when available
---
processing/watermark.go | 2 +-
vips/vips.c | 25 +++++++++++++++++--------
vips/vips.go | 4 ++--
vips/vips.h | 2 ++
4 files changed, 22 insertions(+), 11 deletions(-)
diff --git a/processing/watermark.go b/processing/watermark.go
index db5ba8410e..23845a53de 100644
--- a/processing/watermark.go
+++ b/processing/watermark.go
@@ -69,7 +69,7 @@ func prepareWatermark(wm *vips.Image, wmData *imagedata.ImageData, opts *options
}
}
- wm.RemoveHeader("palette-bit-depth")
+ wm.RemoveBitsPerSampleHeader()
return nil
}
diff --git a/vips/vips.c b/vips/vips.c
index 4178ff29b2..e6cce86ad4 100644
--- a/vips/vips.c
+++ b/vips/vips.c
@@ -15,6 +15,10 @@
#define VIPS_GIF_RESOLUTION_LIMITED \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION <= 12)
+#ifndef VIPS_META_BITS_PER_SAMPLE
+#define VIPS_META_BITS_PER_SAMPLE "palette-bit-depth"
+#endif
+
int
vips_initialize() {
return vips_init("imgproxy");
@@ -134,17 +138,22 @@ vips_get_orientation(VipsImage *image) {
}
int
-vips_get_palette_bit_depth(VipsImage *image) {
- int palette_bit_depth;
+vips_get_bits_per_sample(VipsImage *image) {
+ int bits_per_sample;
if (
- vips_image_get_typeof(image, "palette-bit-depth") == G_TYPE_INT &&
- vips_image_get_int(image, "palette-bit-depth", &palette_bit_depth) == 0
- ) return palette_bit_depth;
+ vips_image_get_typeof(image, VIPS_META_BITS_PER_SAMPLE) == G_TYPE_INT &&
+ vips_image_get_int(image, VIPS_META_BITS_PER_SAMPLE, &bits_per_sample) == 0
+ ) return bits_per_sample;
return 0;
}
+void
+vips_remove_bits_per_sample(VipsImage *image) {
+ vips_image_remove(image, VIPS_META_BITS_PER_SAMPLE);
+}
+
VipsBandFormat
vips_band_format(VipsImage *in) {
return in->BandFmt;
@@ -610,7 +619,7 @@ vips_strip(VipsImage *in, VipsImage **out, int keep_exif_copyright) {
if (
(strcmp(name, VIPS_META_ICC_NAME) == 0) ||
- (strcmp(name, "palette-bit-depth") == 0) ||
+ (strcmp(name, VIPS_META_BITS_PER_SAMPLE) == 0) ||
(strcmp(name, "width") == 0) ||
(strcmp(name, "height") == 0) ||
(strcmp(name, "bands") == 0) ||
@@ -664,8 +673,8 @@ vips_pngsave_go(VipsImage *in, void **buf, size_t *len, int interlace, int quant
else if (colors > 4) bitdepth = 4;
else if (colors > 2) bitdepth = 2;
} else {
- bitdepth = vips_get_palette_bit_depth(in);
- if (bitdepth) {
+ bitdepth = vips_get_bits_per_sample(in);
+ if (bitdepth && bitdepth <= 8) {
if (bitdepth > 4) bitdepth = 8;
else if (bitdepth > 2) bitdepth = 4;
quantize = 1;
diff --git a/vips/vips.go b/vips/vips.go
index 2dee4b683f..954e0abcdf 100644
--- a/vips/vips.go
+++ b/vips/vips.go
@@ -482,8 +482,8 @@ func (img *Image) SetBlob(name string, value []byte) {
C.vips_image_set_blob_copy(img.VipsImage, cachedCString(name), unsafe.Pointer(&value[0]), C.size_t(len(value)))
}
-func (img *Image) RemoveHeader(name string) {
- C.vips_image_remove(img.VipsImage, cachedCString(name))
+func (img *Image) RemoveBitsPerSampleHeader() {
+ C.vips_remove_bits_per_sample(img.VipsImage)
}
func (img *Image) CastUchar() error {
diff --git a/vips/vips.h b/vips/vips.h
index 7d84539a8c..c4071442c0 100644
--- a/vips/vips.h
+++ b/vips/vips.h
@@ -28,6 +28,8 @@ void vips_strip_meta(VipsImage *image);
VipsBandFormat vips_band_format(VipsImage *in);
+void vips_remove_bits_per_sample(VipsImage * image);
+
gboolean vips_is_animated(VipsImage * in);
int vips_image_get_array_int_go(VipsImage *image, const char *name, int **out, int *n);
From 39c436e5273a6d2e0d5fe6627fb9602fb0e53ba2 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 23 May 2023 19:09:30 +0300
Subject: [PATCH 010/138] Preserve GIF bit-per-sample
---
CHANGELOG.md | 3 +++
vips/vips.c | 4 +++-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3bbc1957c..137a61d284 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
### Add
- Add `IMGPROXY_URL_REPLACEMENTS` config.
+### Change
+- Preserve GIF's bit-per-sample.
+
## [3.17.0] - 2023-05-10
### Add
- Add `process_resident_memory_bytes`, `process_virtual_memory_bytes`, `go_memstats_sys_bytes`, `go_memstats_heap_idle_bytes`, `go_memstats_heap_inuse_bytes`, `go_goroutines`, `go_threads`, `buffer_default_size_bytes`, `buffer_max_size_bytes`, and `buffer_size_bytes` metrics to OpenTelemetry.
diff --git a/vips/vips.c b/vips/vips.c
index e6cce86ad4..2a4cfb0a25 100644
--- a/vips/vips.c
+++ b/vips/vips.c
@@ -712,7 +712,9 @@ vips_webpsave_go(VipsImage *in, void **buf, size_t *len, int quality) {
int
vips_gifsave_go(VipsImage *in, void **buf, size_t *len) {
#if VIPS_SUPPORT_GIFSAVE
- return vips_gifsave_buffer(in, buf, len, NULL);
+ int bitdepth = vips_get_bits_per_sample(in);
+ if (bitdepth <= 0 || bitdepth > 8 ) bitdepth = 8;
+ return vips_gifsave_buffer(in, buf, len, "bitdepth", bitdepth, NULL);
#else
vips_error("vips_gifsave_go", "Saving GIF is not supported (libvips 8.12+ reuired)");
return 1;
From 5a7b6068c79569366080d5966492a7f6e790de2b Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 23 May 2023 19:10:05 +0300
Subject: [PATCH 011/138] Update base Docker image
---
docker/Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 60b9d26076..4662f164f1 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE_VERSION="v3.5.0"
+ARG BASE_IMAGE_VERSION="v3.5.1"
FROM darthsim/imgproxy-base:${BASE_IMAGE_VERSION}
From 524434b63fa10d60cb748b6627f3e29a4a29dfcb Mon Sep 17 00:00:00 2001
From: Niklas Mollenhauer
Date: Wed, 24 May 2023 15:30:00 +0200
Subject: [PATCH 012/138] Drop dependency + use built-in `base64url` (#1156)
* Drop dependency + use built-in `base64url`
[`create-hmac`](https://github.com/browserify/createHmac) is meant for compat between node and browser compat and was last updated in 2018.
Computing the HMAC signature on the client (browser) does not make any sense in 99% of the use-cases, as the secrets would be needed on the client side.
This means that we can drop browser support and just use the native node module, which is also exported by the `create-hmac` when running on node.
`Buffer.toString()` also accepts "base64url" as an encoding, so we can drop the `urlSafeBase64` in favor of that.
* Use encoder from hmac instance
---
examples/signature.js | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/examples/signature.js b/examples/signature.js
index ecbbce759c..06eb08e96c 100644
--- a/examples/signature.js
+++ b/examples/signature.js
@@ -1,19 +1,16 @@
-const createHmac = require('create-hmac')
+import { createHmac } from 'node:crypto';
const KEY = '943b421c9eb07c830af81030552c86009268de4e532ba2ee2eab8247c6da0881'
const SALT = '520f986b998545b4785e0defbc4f3c1203f22de2374a3d53cb7a7fe9fea309c5'
-const urlSafeBase64 = (string) => {
- return Buffer.from(string).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
-}
-
const hexDecode = (hex) => Buffer.from(hex, 'hex')
const sign = (salt, target, secret) => {
const hmac = createHmac('sha256', hexDecode(secret))
hmac.update(hexDecode(salt))
hmac.update(target)
- return urlSafeBase64(hmac.digest())
+
+ return hmac.digest('base64url')
}
const path = "/rs:fit:300:300/plain/http://img.example.com/pretty/image.jpg"
From b3905c0cd373adc37dc3fb7d6707cca5e9d5d451 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Sun, 28 May 2023 19:35:29 +0300
Subject: [PATCH 013/138] Respond with 422 on error during image loading
---
CHANGELOG.md | 1 +
vips/vips.go | 10 +++++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 137a61d284..85e8fe68b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
### Change
- Preserve GIF's bit-per-sample.
+- Respond with 422 on error during image loading.
## [3.17.0] - 2023-05-10
### Add
diff --git a/vips/vips.go b/vips/vips.go
index 954e0abcdf..126692a863 100644
--- a/vips/vips.go
+++ b/vips/vips.go
@@ -11,6 +11,7 @@ import (
"math"
"os"
"runtime"
+ "strings"
"sync"
"unsafe"
@@ -152,7 +153,14 @@ func Cleanup() {
func Error() error {
defer C.vips_error_clear()
- return ierrors.NewUnexpected(C.GoString(C.vips_error_buffer()), 1)
+
+ errstr := strings.TrimSpace(C.GoString(C.vips_error_buffer()))
+
+ if strings.Contains(errstr, "load_buffer: ") {
+ return ierrors.New(422, errstr, "Broken or unsupported image")
+ }
+
+ return ierrors.NewUnexpected(errstr, 1)
}
func hasOperation(name string) bool {
From a246eda065aa7894afe516c956f0e570da3424e7 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Sun, 28 May 2023 19:57:50 +0300
Subject: [PATCH 014/138] Ensure URL replacements are executed in the specified
order
---
config/config.go | 6 ++++--
config/configurators/configurators.go | 30 +++++++++++++++++----------
options/processing_options_test.go | 10 +++++----
options/url.go | 4 ++--
4 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/config/config.go b/config/config.go
index 05ded60deb..073d186c1f 100644
--- a/config/config.go
+++ b/config/config.go
@@ -16,6 +16,8 @@ import (
"github.com/imgproxy/imgproxy/v3/version"
)
+type URLReplacement = configurators.URLReplacement
+
var (
Network string
Bind string
@@ -127,7 +129,7 @@ var (
LastModifiedEnabled bool
BaseURL string
- URLReplacements map[*regexp.Regexp]string
+ URLReplacements []URLReplacement
Presets []string
OnlyPresets bool
@@ -318,7 +320,7 @@ func Reset() {
LastModifiedEnabled = false
BaseURL = ""
- URLReplacements = make(map[*regexp.Regexp]string)
+ URLReplacements = make([]URLReplacement, 0)
Presets = make([]string, 0)
OnlyPresets = false
diff --git a/config/configurators/configurators.go b/config/configurators/configurators.go
index de3052a1d2..5ea47c3835 100644
--- a/config/configurators/configurators.go
+++ b/config/configurators/configurators.go
@@ -12,6 +12,11 @@ import (
"github.com/imgproxy/imgproxy/v3/imagetype"
)
+type URLReplacement struct {
+ Regexp *regexp.Regexp
+ Replacement string
+}
+
func Int(i *int, name string) {
if env, err := strconv.Atoi(os.Getenv(name)); err == nil {
*i = env
@@ -240,21 +245,24 @@ func Patterns(s *[]*regexp.Regexp, name string) {
}
}
-func Replacements(m *map[*regexp.Regexp]string, name string) error {
- var sm map[string]string
-
- if err := StringMap(&sm, name); err != nil {
- return err
- }
+func Replacements(s *[]URLReplacement, name string) error {
+ if env := os.Getenv(name); len(env) > 0 {
+ ss := []URLReplacement(nil)
- if len(sm) > 0 {
- mm := make(map[*regexp.Regexp]string)
+ keyvalues := strings.Split(env, ";")
- for k, v := range sm {
- mm[RegexpFromPattern(k)] = v
+ for _, keyvalue := range keyvalues {
+ parts := strings.SplitN(keyvalue, "=", 2)
+ if len(parts) != 2 {
+ return fmt.Errorf("Invalid key/value: %s", keyvalue)
+ }
+ ss = append(ss, URLReplacement{
+ Regexp: RegexpFromPattern(parts[0]),
+ Replacement: parts[1],
+ })
}
- *m = mm
+ *s = ss
}
return nil
diff --git a/options/processing_options_test.go b/options/processing_options_test.go
index 2a48fa5a2a..2c1416a6b9 100644
--- a/options/processing_options_test.go
+++ b/options/processing_options_test.go
@@ -56,8 +56,9 @@ func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithBase() {
}
func (s *ProcessingOptionsTestSuite) TestParseBase64URLWithReplacement() {
- config.URLReplacements = map[*regexp.Regexp]string{
- regexp.MustCompile("^test://([^/]*)/"): "http://images.dev/${1}/dolor/",
+ config.URLReplacements = []config.URLReplacement{
+ {Regexp: regexp.MustCompile("^test://([^/]*)/"), Replacement: "test2://images.dev/${1}/dolor/"},
+ {Regexp: regexp.MustCompile("^test2://"), Replacement: "http://"},
}
originURL := "test://lorem/ipsum.jpg?param=value"
@@ -112,8 +113,9 @@ func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithBase() {
}
func (s *ProcessingOptionsTestSuite) TestParsePlainURLWithReplacement() {
- config.URLReplacements = map[*regexp.Regexp]string{
- regexp.MustCompile("^test://([^/]*)/"): "http://images.dev/${1}/dolor/",
+ config.URLReplacements = []config.URLReplacement{
+ {Regexp: regexp.MustCompile("^test://([^/]*)/"), Replacement: "test2://images.dev/${1}/dolor/"},
+ {Regexp: regexp.MustCompile("^test2://"), Replacement: "http://"},
}
originURL := "test://lorem/ipsum.jpg"
diff --git a/options/url.go b/options/url.go
index b4e8e5cc95..e3ba82f94c 100644
--- a/options/url.go
+++ b/options/url.go
@@ -13,8 +13,8 @@ import (
const urlTokenPlain = "plain"
func preprocessURL(u string) string {
- for re, repl := range config.URLReplacements {
- u = re.ReplaceAllString(u, repl)
+ for _, repl := range config.URLReplacements {
+ u = repl.Regexp.ReplaceAllString(u, repl.Replacement)
}
if len(config.BaseURL) == 0 || strings.HasPrefix(u, config.BaseURL) {
From 2e6a3c6dd110f859cae917721e9ac43e9e0935ec Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Mon, 29 May 2023 19:48:39 +0300
Subject: [PATCH 015/138] Separate Photoshop and IPTC metadata parsing/dumping
---
imagemeta/iptc/iptc.go | 73 ++--------------------------
imagemeta/photoshop/photoshop.go | 81 ++++++++++++++++++++++++++++++++
processing/strip_metadata.go | 31 ++++++++----
3 files changed, 107 insertions(+), 78 deletions(-)
create mode 100644 imagemeta/photoshop/photoshop.go
diff --git a/imagemeta/iptc/iptc.go b/imagemeta/iptc/iptc.go
index 88fa1e1898..f8148b82fd 100644
--- a/imagemeta/iptc/iptc.go
+++ b/imagemeta/iptc/iptc.go
@@ -10,13 +10,9 @@ import (
)
var (
- ps3Header = []byte("Photoshop 3.0\x00")
- ps3BlockHeader = []byte("8BIM")
- ps3IptcRecourceID = []byte("\x04\x04")
- iptcTagHeader = byte(0x1c)
+ iptcTagHeader = byte(0x1c)
- errInvalidPS3Header = errors.New("invalid Photoshop 3.0 header")
- errInvalidDataSize = errors.New("invalid IPTC data size")
+ errInvalidDataSize = errors.New("invalid IPTC data size")
)
type IptcMap map[TagKey][]TagValue
@@ -69,7 +65,7 @@ func (m IptcMap) MarshalJSON() ([]byte, error) {
return json.Marshal(mm)
}
-func ParseTags(data []byte, m IptcMap) error {
+func Parse(data []byte, m IptcMap) error {
buf := bytes.NewBuffer(data)
// Min tag size is 5 (2 tagHeader)
@@ -114,52 +110,7 @@ func ParseTags(data []byte, m IptcMap) error {
return nil
}
-func ParsePS3(data []byte, m IptcMap) error {
- buf := bytes.NewBuffer(data)
-
- if !bytes.Equal(buf.Next(14), ps3Header) {
- return errInvalidPS3Header
- }
-
- // Read blocks
- // Minimal block size is 12 (4 blockHeader + 2 resoureceID + 2 name + 4 blockSize)
- for buf.Len() >= 12 {
- if !bytes.Equal(buf.Bytes()[:4], ps3BlockHeader) {
- buf.Next(1)
- continue
- }
-
- // Skip block header
- buf.Next(4)
-
- resoureceID := buf.Next(2)
-
- // Skip name
- // Name is zero terminated string padded to even
- for buf.Len() > 0 && buf.Next(2)[1] != 0 {
- }
-
- if buf.Len() < 4 {
- break
- }
-
- blockSize := int(binary.BigEndian.Uint32(buf.Next(4)))
-
- if buf.Len() < blockSize {
- break
- }
- blockData := buf.Next(blockSize)
-
- // 1028 is IPTC tags block
- if bytes.Equal(resoureceID, ps3IptcRecourceID) {
- return ParseTags(blockData, m)
- }
- }
-
- return nil
-}
-
-func (m IptcMap) DumpTags() []byte {
+func (m IptcMap) Dump() []byte {
buf := new(bytes.Buffer)
for key, values := range m {
@@ -187,19 +138,3 @@ func (m IptcMap) DumpTags() []byte {
return buf.Bytes()
}
-
-func (m IptcMap) Dump() []byte {
- tagsDump := m.DumpTags()
-
- buf := new(bytes.Buffer)
- buf.Grow(26)
-
- buf.Write(ps3Header)
- buf.Write(ps3BlockHeader)
- buf.Write(ps3IptcRecourceID)
- buf.Write([]byte{0, 0})
- binary.Write(buf, binary.BigEndian, uint32(len(tagsDump)))
- buf.Write(tagsDump)
-
- return buf.Bytes()
-}
diff --git a/imagemeta/photoshop/photoshop.go b/imagemeta/photoshop/photoshop.go
new file mode 100644
index 0000000000..297e180678
--- /dev/null
+++ b/imagemeta/photoshop/photoshop.go
@@ -0,0 +1,81 @@
+package photoshop
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+)
+
+var (
+ ps3Header = []byte("Photoshop 3.0\x00")
+ ps3BlockHeader = []byte("8BIM")
+
+ errInvalidPS3Header = errors.New("invalid Photoshop 3.0 header")
+)
+
+const (
+ IptcKey = "\x04\x04"
+ ResolutionKey = "\x03\xed"
+)
+
+type PhotoshopMap map[string][]byte
+
+func Parse(data []byte, m PhotoshopMap) error {
+ buf := bytes.NewBuffer(data)
+
+ if !bytes.Equal(buf.Next(14), ps3Header) {
+ return errInvalidPS3Header
+ }
+
+ // Read blocks
+ // Minimal block size is 12 (4 blockHeader + 2 resoureceID + 2 name + 4 blockSize)
+ for buf.Len() >= 12 {
+ if !bytes.Equal(buf.Bytes()[:4], ps3BlockHeader) {
+ buf.Next(1)
+ continue
+ }
+
+ // Skip block header
+ buf.Next(4)
+
+ resoureceID := buf.Next(2)
+
+ // Skip name
+ // Name is zero terminated string padded to even
+ for buf.Len() > 0 && buf.Next(2)[1] != 0 {
+ }
+
+ if buf.Len() < 4 {
+ break
+ }
+
+ blockSize := int(binary.BigEndian.Uint32(buf.Next(4)))
+
+ if buf.Len() < blockSize {
+ break
+ }
+ blockData := buf.Next(blockSize)
+
+ m[string(resoureceID)] = blockData
+ }
+
+ return nil
+}
+
+func (m PhotoshopMap) Dump() []byte {
+ buf := new(bytes.Buffer)
+ buf.Grow(26)
+
+ buf.Write(ps3Header)
+ buf.Write(ps3BlockHeader)
+
+ for id, data := range m {
+ buf.WriteString(id)
+ // Write empty name
+ buf.Write([]byte{0, 0})
+ binary.Write(buf, binary.BigEndian, uint32(len(data)))
+ buf.Write(data)
+ }
+
+ return buf.Bytes()
+}
diff --git a/processing/strip_metadata.go b/processing/strip_metadata.go
index a30f05415b..d346546d6b 100644
--- a/processing/strip_metadata.go
+++ b/processing/strip_metadata.go
@@ -7,18 +7,27 @@ import (
"github.com/imgproxy/imgproxy/v3/imagedata"
"github.com/imgproxy/imgproxy/v3/imagemeta/iptc"
+ "github.com/imgproxy/imgproxy/v3/imagemeta/photoshop"
"github.com/imgproxy/imgproxy/v3/options"
"github.com/imgproxy/imgproxy/v3/vips"
)
-func stripIPTC(img *vips.Image) []byte {
- iptcData, err := img.GetBlob("iptc-data")
- if err != nil || len(iptcData) == 0 {
+func stripPS3(img *vips.Image) []byte {
+ ps3Data, err := img.GetBlob("iptc-data")
+ if err != nil || len(ps3Data) == 0 {
+ return nil
+ }
+
+ ps3Map := make(photoshop.PhotoshopMap)
+ photoshop.Parse(ps3Data, ps3Map)
+
+ iptcData, found := ps3Map[photoshop.IptcKey]
+ if !found {
return nil
}
iptcMap := make(iptc.IptcMap)
- err = iptc.ParsePS3(iptcData, iptcMap)
+ err = iptc.Parse(iptcData, iptcMap)
if err != nil {
return nil
}
@@ -33,7 +42,11 @@ func stripIPTC(img *vips.Image) []byte {
return nil
}
- return iptcMap.Dump()
+ ps3Map = photoshop.PhotoshopMap{
+ photoshop.IptcKey: iptcMap.Dump(),
+ }
+
+ return ps3Map.Dump()
}
func stripXMP(img *vips.Image) []byte {
@@ -97,10 +110,10 @@ func stripMetadata(pctx *pipelineContext, img *vips.Image, po *options.Processin
return nil
}
- var iptcData, xmpData []byte
+ var ps3Data, xmpData []byte
if po.KeepCopyright {
- iptcData = stripIPTC(img)
+ ps3Data = stripPS3(img)
xmpData = stripXMP(img)
}
@@ -109,8 +122,8 @@ func stripMetadata(pctx *pipelineContext, img *vips.Image, po *options.Processin
}
if po.KeepCopyright {
- if len(iptcData) > 0 {
- img.SetBlob("iptc-data", iptcData)
+ if len(ps3Data) > 0 {
+ img.SetBlob("iptc-data", ps3Data)
}
if len(xmpData) > 0 {
From b20b5ff7684f7e63d1a90dc24ec8aa2412c7c128 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Mon, 29 May 2023 21:27:47 +0300
Subject: [PATCH 016/138] Fix Photoshop metadata dump
---
imagemeta/photoshop/photoshop.go | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/imagemeta/photoshop/photoshop.go b/imagemeta/photoshop/photoshop.go
index 297e180678..625d61766a 100644
--- a/imagemeta/photoshop/photoshop.go
+++ b/imagemeta/photoshop/photoshop.go
@@ -67,9 +67,13 @@ func (m PhotoshopMap) Dump() []byte {
buf.Grow(26)
buf.Write(ps3Header)
- buf.Write(ps3BlockHeader)
for id, data := range m {
+ if len(data) == 0 {
+ continue
+ }
+
+ buf.Write(ps3BlockHeader)
buf.WriteString(id)
// Write empty name
buf.Write([]byte{0, 0})
From ac87eea3174a67f03b553b7b754af6d674ea9fc7 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 30 May 2023 20:08:04 +0300
Subject: [PATCH 017/138] Update changelog and docs
---
CHANGELOG.md | 4 ++++
docs/configuration.md | 1 +
docs/generating_the_url.md | 12 ++++++++++++
docs/getting_the_image_info.md | 11 +++++++++++
4 files changed, 28 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 85e8fe68b7..06ae16f553 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,10 @@
## [Unreleased]
### Add
- Add `IMGPROXY_URL_REPLACEMENTS` config.
+- (pro) Add `IMGPROXY_STRIP_METADATA_DPI` config.
+- (pro) Add [dpi](https://docs.imgproxy.net/latest/configuration?id=dpi) processing option.
+- (pro) Add WebP EXIF and XMP to the `/info` response.
+- (pro) Add Photoshop resolution data to the `/info` response.
### Change
- Preserve GIF's bit-per-sample.
diff --git a/docs/configuration.md b/docs/configuration.md
index 0d3bcb713f..9515e5f40d 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -541,6 +541,7 @@ imgproxy can send logs to syslog, but this feature is disabled by default. To en
* `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEGs and WebP files. Allows processing the entire image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images.
* `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true`
* `IMGPROXY_KEEP_COPYRIGHT`: when `true`, imgproxy will not remove copyright info while stripping metadata. Default: `true`
+* `IMGPROXY_STRIP_METADATA_DPI`: ![pro](./assets/pro.svg) the DPI metadata value that should be set for the image when its metadata is stripped. Default: `72.0`
* `IMGPROXY_STRIP_COLOR_PROFILE`: when `true`, imgproxy will transform the embedded color profile (ICC) to sRGB and remove it from the image. Otherwise, imgproxy will try to keep it as is. Default: `true`
* `IMGPROXY_AUTO_ROTATE`: when `true`, imgproxy will automatically rotate images based on the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image in all cases. Default: `true`
* `IMGPROXY_ENFORCE_THUMBNAIL`: when `true` and the source image has an embedded thumbnail, imgproxy will always use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded in `heic` and `avif` are supported. Default: `false`
diff --git a/docs/generating_the_url.md b/docs/generating_the_url.md
index 8cb4752cb5..1f9d7a62e3 100644
--- a/docs/generating_the_url.md
+++ b/docs/generating_the_url.md
@@ -552,6 +552,18 @@ kcr:%keep_copyright
When set to `1`, `t` or `true`, imgproxy will not remove copyright info while stripping metadata. This is normally controlled by the [IMGPROXY_KEEP_COPYRIGHT](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request.
+### DPI![pro](./assets/pro.svg) :id=dpi
+
+```
+dpi:%dpi
+```
+
+When set, imgproxy will replace the image's DPI metadata with the provided value. When set to `0`, imgproxy won't change the image's DPI or will reset it to the default value if the image's metadata should be stripped.
+
+**📝 Note:** This processing option takes effect whether imgproxy should strip the image's metadata or not.
+
+Default: `0`
+
### Strip color profile
```
diff --git a/docs/getting_the_image_info.md b/docs/getting_the_image_info.md
index ccabbfa2ee..5d992d7cd0 100644
--- a/docs/getting_the_image_info.md
+++ b/docs/getting_the_image_info.md
@@ -55,6 +55,7 @@ imgproxy responses with a JSON body and returns the following info:
* `exif`: Exif data
* `iptc`: IPTC data
* `xmp`: XMP data
+* `photoshop`: Photoshop metadata (currently, only the resolution data)
* `video_meta`: metadata from the video
**📝 Note:** There are lots of IPTC tags in the spec, but imgproxy supports only a few of them. If you need some tags to be supported, just contact us.
@@ -98,6 +99,16 @@ imgproxy responses with a JSON body and returns the following info:
"photoshop": {
"DateCreated": "2016-09-11T18:44:50.003"
}
+ },
+ "photoshop": {
+ "resolution": {
+ "XResolution": 240,
+ "XResolutionUnit": "inches",
+ "WidthUnit": "inches",
+ "YResolution": 240,
+ "YResolutionUnit": "inches",
+ "HeightUnit": "inches"
+ }
}
}
```
From 597673b6502a447ef368ec85dac95fd1234ef284 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 30 May 2023 20:10:01 +0300
Subject: [PATCH 018/138] Fix changelog
---
CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 06ae16f553..80b0f516d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
### Add
- Add `IMGPROXY_URL_REPLACEMENTS` config.
- (pro) Add `IMGPROXY_STRIP_METADATA_DPI` config.
-- (pro) Add [dpi](https://docs.imgproxy.net/latest/configuration?id=dpi) processing option.
+- (pro) Add [dpi](https://docs.imgproxy.net/latest/generating_the_url?id=dpi) processing option.
- (pro) Add WebP EXIF and XMP to the `/info` response.
- (pro) Add Photoshop resolution data to the `/info` response.
From b9e0134f2e9371c7dead35c22b9e257c8f78e61a Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 31 May 2023 22:52:36 +0300
Subject: [PATCH 019/138] Update changelog
---
CHANGELOG.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 80b0f516d7..29faa1fe37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,9 @@
- Preserve GIF's bit-per-sample.
- Respond with 422 on error during image loading.
+### Fix
+- (pro) Fix applying the `resizing_algorithm` processing option when resizing images with an alpha channel.
+
## [3.17.0] - 2023-05-10
### Add
- Add `process_resident_memory_bytes`, `process_virtual_memory_bytes`, `go_memstats_sys_bytes`, `go_memstats_heap_idle_bytes`, `go_memstats_heap_inuse_bytes`, `go_goroutines`, `go_threads`, `buffer_default_size_bytes`, `buffer_max_size_bytes`, and `buffer_size_bytes` metrics to OpenTelemetry.
From 8629c5eca1e422908363f471513bfc887d778a85 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Wed, 31 May 2023 22:55:51 +0300
Subject: [PATCH 020/138] Bump version
---
CHANGELOG.md | 2 ++
version/version.go | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29faa1fe37..9baf37cf5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+
+## [3.18.0] - 2023-05-31
### Add
- Add `IMGPROXY_URL_REPLACEMENTS` config.
- (pro) Add `IMGPROXY_STRIP_METADATA_DPI` config.
diff --git a/version/version.go b/version/version.go
index 8fe870f3d3..ba9864d0c1 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,6 +1,6 @@
package version
-const version = "3.17.0"
+const version = "3.18.0"
func Version() string {
return version
From 9b6074094a83eef54797fc269beb667932483b76 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 6 Jun 2023 21:09:42 +0300
Subject: [PATCH 021/138] Fix return value from vips_resize_go
---
vips/vips.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vips/vips.c b/vips/vips.c
index 2a4cfb0a25..353cbcf6e4 100644
--- a/vips/vips.c
+++ b/vips/vips.c
@@ -220,7 +220,7 @@ vips_resize_go(VipsImage *in, VipsImage **out, double wscale, double hscale) {
clear_image(&base);
- return 0;
+ return res;
}
int
From 8227592451b70e96127af526e80868ef1c33902c Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Mon, 12 Jun 2023 22:00:55 +0300
Subject: [PATCH 022/138] Optimize bufpool
---
bufpool/bufpool.go | 24 ++++++++++++++++++++++--
imagedata/read.go | 4 +---
2 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/bufpool/bufpool.go b/bufpool/bufpool.go
index 6eb97c9237..2cd0f054c4 100644
--- a/bufpool/bufpool.go
+++ b/bufpool/bufpool.go
@@ -106,7 +106,7 @@ func (p *Pool) calibrateAndClean() {
for i := 0; i < steps; i++ {
callsSum += p.tmpCalls[i]
- if callsSum > defSum || defStep < 0 {
+ if defStep < 0 && callsSum > defSum {
defStep = i
}
@@ -219,7 +219,7 @@ func (p *Pool) Get(size int, grow bool) *bytes.Buffer {
growSize := p.defaultSize
if grow {
- growSize = imath.Max(size, growSize)
+ growSize = imath.Max(p.normalizeCap(size), growSize)
}
// Grow the buffer only if we know the requested size and it is smaller than
@@ -261,6 +261,26 @@ func (p *Pool) Put(buf *bytes.Buffer) {
p.insert(buf)
}
+// GrowBuffer growth capacity of the buffer to the normalized provided value
+func (p *Pool) GrowBuffer(buf *bytes.Buffer, cap int) {
+ cap = p.normalizeCap(cap)
+ if buf.Cap() < cap {
+ buf.Grow(cap - buf.Len())
+ }
+}
+
+func (p *Pool) normalizeCap(cap int) int {
+ // Don't normalize cap if it's larger than maxSize
+ // since we'll throw this buf out anyway
+ maxSize := int(atomic.LoadUint64(&p.maxSize))
+ if maxSize > 0 && cap > maxSize {
+ return cap
+ }
+
+ ind := index(cap)
+ return imath.Max(cap, minSize< buf.Cap() {
- buf.Grow(contentLength - buf.Len())
- }
+ downloadBufPool.GrowBuffer(buf, contentLength)
if err = br.Flush(); err != nil {
buf.Reset()
From 56b294cbd3bcd18334c55b20e0ecac8531f72467 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Sat, 17 Jun 2023 20:15:11 +0300
Subject: [PATCH 023/138] Update docs
---
docs/generating_the_url.md | 2 +-
docs/getting_the_image_info.md | 2 +-
docs/signing_the_url.md | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/generating_the_url.md b/docs/generating_the_url.md
index 1f9d7a62e3..e89031225b 100644
--- a/docs/generating_the_url.md
+++ b/docs/generating_the_url.md
@@ -885,7 +885,7 @@ When using an encoded source URL, you can specify the [extension](#extension) af
/aHR0cDovL2V4YW1w/bGUuY29tL2ltYWdl/cy9jdXJpb3NpdHku/anBn.png
```
-### Encrypted with AES-CBC
+### Encrypted with AES-CBC![pro](./assets/pro.svg) :id=encrypted-with-aes-cbc
The source URL can be encrypted with the AES-CBC algorithm, prepended by the `/enc/` segment. The encrypted URL can be split with `/` as desired:
diff --git a/docs/getting_the_image_info.md b/docs/getting_the_image_info.md
index 5d992d7cd0..4121bc52ce 100644
--- a/docs/getting_the_image_info.md
+++ b/docs/getting_the_image_info.md
@@ -36,7 +36,7 @@ The source URL can be encoded with URL-safe Base64. The encoded URL can be split
/aHR0cDovL2V4YW1w/bGUuY29tL2ltYWdl/cy9jdXJpb3NpdHku/anBn
```
-#### Encrypted with AES-CBC
+#### Encrypted with AES-CBC![pro](./assets/pro.svg) :id=encrypted-with-aes-cbc
The source URL can be encrypted with the AES-CBC algorithm, prepended by the `/enc/` segment. The encrypted URL can be split with `/` as desired:
diff --git a/docs/signing_the_url.md b/docs/signing_the_url.md
index fc98b3ac72..1d7f6d4966 100644
--- a/docs/signing_the_url.md
+++ b/docs/signing_the_url.md
@@ -23,8 +23,8 @@ A signature is a URL-safe Base64-encoded HMAC digest of the rest of the path, in
* Take the part of the path after the signature:
- * For [processing URLs](generating_the_url.md): `/%processing_options/%encoded_url.%extension` or `/%processing_options/plain/%plain_url@%extension`
- * For [info URLs](getting_the_image_info.md): `/%encoded_url` or `/plain/%plain_url`
+ * For [processing URLs](generating_the_url.md): `/%processing_options/%encoded_url.%extension`, `/%processing_options/plain/%plain_url@%extension`, or `/%processing_options/enc/%encrypted_url.%extension`
+ * For [info URLs](getting_the_image_info.md): `/%encoded_url`, `/plain/%plain_url`, or `/enc/%encrypted_url`
* Add a salt to the beginning.
* Calculate the HMAC digest using SHA256.
* Encode the result with URL-safe Base64.
From 9175c66feeaf25729ef2e8b5ca62763e920a51ac Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Thu, 29 Jun 2023 20:05:24 +0300
Subject: [PATCH 024/138] Update maximum and default AVIF speed
---
CHANGELOG.md | 2 ++
config/config.go | 6 +++---
docs/configuration.md | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9baf37cf5a..9755de0c6e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+### Change
+- Change maximum and default values of `IMGPROXY_AVIF_SPEED` to `9`.
## [3.18.0] - 2023-05-31
### Add
diff --git a/config/config.go b/config/config.go
index 073d186c1f..1397546dd0 100644
--- a/config/config.go
+++ b/config/config.go
@@ -242,7 +242,7 @@ func Reset() {
PngInterlaced = false
PngQuantize = false
PngQuantizationColors = 256
- AvifSpeed = 8
+ AvifSpeed = 9
Quality = 80
FormatQuality = map[imagetype.Type]int{imagetype.AVIF: 65}
StripMetadata = true
@@ -658,8 +658,8 @@ func Configure() error {
if AvifSpeed < 0 {
return fmt.Errorf("Avif speed should be greater than 0, now - %d\n", AvifSpeed)
- } else if AvifSpeed > 8 {
- return fmt.Errorf("Avif speed can't be greater than 8, now - %d\n", AvifSpeed)
+ } else if AvifSpeed > 9 {
+ return fmt.Errorf("Avif speed can't be greater than 9, now - %d\n", AvifSpeed)
}
if Quality <= 0 {
diff --git a/docs/configuration.md b/docs/configuration.md
index 9515e5f40d..c5c8e3c678 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -176,7 +176,7 @@ When cookie forwarding is activated, by default, imgproxy assumes the scope of t
### Advanced AVIF compression
-* `IMGPROXY_AVIF_SPEED`: controls the CPU effort spent improving compression. The lowest speed is at 0 and the fastest is at 8. Default: `8`
+* `IMGPROXY_AVIF_SPEED`: controls the CPU effort spent improving compression. The lowest speed is at 0 and the fastest is at 9. Default: `9`
### Autoquality
From 9c1a3befd105dc955c482011532300b0afaf49cb Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Thu, 29 Jun 2023 20:16:55 +0300
Subject: [PATCH 025/138] Update changelog
---
CHANGELOG.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9755de0c6e..08c748990f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
## [Unreleased]
### Change
- Change maximum and default values of `IMGPROXY_AVIF_SPEED` to `9`.
+- (pro) Fix detection of some videos.
+- (pro) Better calculation of the image complexity during choosing the best format.
## [3.18.0] - 2023-05-31
### Add
From d094b57a6fe3db9760c6538653019f63e5932137 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Thu, 29 Jun 2023 20:19:30 +0300
Subject: [PATCH 026/138] Update base Docker image
---
CHANGELOG.md | 1 +
docker/Dockerfile | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08c748990f..3ab9a44205 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
- Change maximum and default values of `IMGPROXY_AVIF_SPEED` to `9`.
- (pro) Fix detection of some videos.
- (pro) Better calculation of the image complexity during choosing the best format.
+- (docker) Fix freezes and crashes introduced in v3.18.0 by liborc.
## [3.18.0] - 2023-05-31
### Add
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 4662f164f1..8167733cad 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE_VERSION="v3.5.1"
+ARG BASE_IMAGE_VERSION="v3.6.0"
FROM darthsim/imgproxy-base:${BASE_IMAGE_VERSION}
From 1dacb3beb13300063b1d5851d56f5cfac3525874 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Thu, 29 Jun 2023 20:20:43 +0300
Subject: [PATCH 027/138] Bump version
---
CHANGELOG.md | 2 ++
version/version.go | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ab9a44205..d07bba78f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+
+## [3.18.1] - 2023-07-29
### Change
- Change maximum and default values of `IMGPROXY_AVIF_SPEED` to `9`.
- (pro) Fix detection of some videos.
diff --git a/version/version.go b/version/version.go
index ba9864d0c1..aa6a312a97 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,6 +1,6 @@
package version
-const version = "3.18.0"
+const version = "3.18.1"
func Version() string {
return version
From 245974121807971172e80b856f4d3e9f8a2d7f5e Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Fri, 30 Jun 2023 17:52:08 +0300
Subject: [PATCH 028/138] Unsharpening => unsharp masking in docs
---
docs/configuration.md | 16 ++++++++--------
docs/generating_the_url.md | 8 ++++----
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/docs/configuration.md b/docs/configuration.md
index c5c8e3c678..1d8e11a72d 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -279,16 +279,16 @@ imgproxy Pro can extract specific video frames to create thumbnails. This featur
Read more about watermarks in the [Watermark](watermark.md) guide.
-## Unsharpening
+## Unsharp masking
-imgproxy Pro can apply an unsharpening mask to your images.
+imgproxy Pro can apply unsharp masking to your images.
-* `IMGPROXY_UNSHARPENING_MODE`: ![pro](./assets/pro.svg) controls when an unsharpenning mask should be applied. The following modes are supported:
- * `auto`: _(default)_ apply an unsharpening mask only when an image is downscaled and the `sharpen` option has not been set.
- * `none`: the unsharpening mask is not applied.
- * `always`: always applies the unsharpening mask.
-* `IMGPROXY_UNSHARPENING_WEIGHT`: ![pro](./assets/pro.svg) a floating-point number that defines how neighboring pixels will affect the current pixel. The greater the value, the sharper the image. This value should be greater than zero. Default: `1`
-* `IMGPROXY_UNSHARPENING_DIVIDOR`: ![pro](./assets/pro.svg) a floating-point number that defines the unsharpening strength. The lesser the value, the sharper the image. This value be greater than zero. Default: `24`
+* `IMGPROXY_UNSHARP_MASKING_MODE`: ![pro](./assets/pro.svg) controls when unsharp masking should be applied. The following modes are supported:
+ * `auto`: _(default)_ apply unsharp masking only when an image is downscaled and the `sharpen` option has not been set.
+ * `none`: unsharp masking is not applied.
+ * `always`: always applies unsharp masking.
+* `IMGPROXY_UNSHARP_MASKING_WEIGHT`: ![pro](./assets/pro.svg) a floating-point number that defines how neighboring pixels will affect the current pixel. The greater the value, the sharper the image. This value should be greater than zero. Default: `1`
+* `IMGPROXY_UNSHARP_MASKING_DIVIDER`: ![pro](./assets/pro.svg) a floating-point number that defines unsharp masking strength. The lesser the value, the sharper the image. This value be greater than zero. Default: `24`
## Smart crop
diff --git a/docs/generating_the_url.md b/docs/generating_the_url.md
index e89031225b..a4572601a3 100644
--- a/docs/generating_the_url.md
+++ b/docs/generating_the_url.md
@@ -397,14 +397,14 @@ When set, imgproxy will apply the pixelate filter to the resulting image. The va
Default: disabled
-### Unsharpening![pro](./assets/pro.svg) :id=unsharpening
+### Unsharp masking![pro](./assets/pro.svg) :id=unsharp-masking
```
-unsharpening:%mode:%weight:%dividor
-ush:%mode:%weight:%dividor
+unsharp_masking:%mode:%weight:%divider
+ush:%mode:%weight:%divider
```
-Allows redefining unsharpening options. All arguments have the same meaning as [Unsharpening](configuration.md#unsharpening) configs. All arguments are optional and can be omitted.
+Allows redefining unsharp masking options. All arguments have the same meaning as [Unsharp masking](configuration.md#unsharp-masking) configs. All arguments are optional and can be omitted.
### Blur detections![pro](./assets/pro.svg) :id=blur-detections
From 136a5d093fa7e9e6047afac42ad3526a68623686 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Sun, 2 Jul 2023 22:58:32 +0300
Subject: [PATCH 029/138] Fix saving to JPEG when using linear colorspace
---
CHANGELOG.md | 2 ++
processing/export_color_profile.go | 6 ++++++
vips/vips.go | 4 ++++
3 files changed, 12 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d07bba78f0..e756417e14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
# Changelog
## [Unreleased]
+### Fix
+- Fix saving to JPEG when using linear colorspace.
## [3.18.1] - 2023-07-29
### Change
diff --git a/processing/export_color_profile.go b/processing/export_color_profile.go
index 4323a37093..d15d869fc3 100644
--- a/processing/export_color_profile.go
+++ b/processing/export_color_profile.go
@@ -9,6 +9,12 @@ import (
func exportColorProfile(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
keepProfile := !po.StripColorProfile && po.Format.SupportsColourProfile()
+ if img.IsLinear() {
+ if err := img.RgbColourspace(); err != nil {
+ return err
+ }
+ }
+
if pctx.iccImported {
if keepProfile {
// We imported ICC profile and want to keep it,
diff --git a/vips/vips.go b/vips/vips.go
index 126692a863..3154ec9626 100644
--- a/vips/vips.go
+++ b/vips/vips.go
@@ -638,6 +638,10 @@ func (img *Image) IsRGB() bool {
format == C.VIPS_INTERPRETATION_RGB16
}
+func (img *Image) IsLinear() bool {
+ return C.vips_image_guess_interpretation(img.VipsImage) == C.VIPS_INTERPRETATION_scRGB
+}
+
func (img *Image) ImportColourProfile() error {
var tmp *C.VipsImage
From 4e4b9edb53dd0ee9aa5c7ee4a1b389de0cc6b0ac Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Sun, 2 Jul 2023 23:01:44 +0300
Subject: [PATCH 030/138] Exclude twitter.com from check-links git hook
---
lefthook.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lefthook.yml b/lefthook.yml
index 43520d292b..83182617e9 100644
--- a/lefthook.yml
+++ b/lefthook.yml
@@ -10,4 +10,4 @@ pre-push:
commands:
check-links:
tags: docs
- run: command -v lychee && lychee docs README.md CHANGELOG.md --exclude localhost --exclude-path docs/index.html
+ run: command -v lychee && lychee docs README.md CHANGELOG.md --exclude localhost --exclude twitter.com --exclude-path docs/index.html
From 4de9b83899ce6c6929e6777c9be616c1e1efa514 Mon Sep 17 00:00:00 2001
From: Andrei Vydrin
Date: Thu, 22 Jun 2023 18:22:10 +0700
Subject: [PATCH 031/138] fix(svg/sanitize): preserve headers from origin data
---
processing_handler.go | 2 +-
processing_handler_test.go | 25 ++++++++++++++++++++++++-
svg/svg.go | 7 ++++---
3 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/processing_handler.go b/processing_handler.go
index a913da4ab0..b8aa5ff089 100644
--- a/processing_handler.go
+++ b/processing_handler.go
@@ -362,7 +362,7 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) {
// Don't process SVG
if originData.Type == imagetype.SVG {
if config.SanitizeSvg {
- sanitized, svgErr := svg.Satitize(originData)
+ sanitized, svgErr := svg.Sanitize(originData)
checkErr(ctx, "svg_processing", svgErr)
// Since we'll replace origin data, it's better to close it to return
diff --git a/processing_handler_test.go b/processing_handler_test.go
index abafff4ec8..f5051e9077 100644
--- a/processing_handler_test.go
+++ b/processing_handler_test.go
@@ -317,13 +317,36 @@ func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() {
require.Equal(s.T(), 200, res.StatusCode)
actual := s.readBody(res)
- expected, err := svg.Satitize(&imagedata.ImageData{Data: s.readTestFile("test1.svg")})
+ expected, err := svg.Sanitize(&imagedata.ImageData{Data: s.readTestFile("test1.svg")})
require.Nil(s.T(), err)
require.True(s.T(), bytes.Equal(expected.Data, actual))
}
+func (s *ProcessingHandlerTestSuite) TestPreserveOriginSVGHeaders() {
+ rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.svg")
+ res := rw.Result()
+
+ require.Equal(s.T(), 200, res.StatusCode)
+
+ actual := s.readBody(res)
+ originHeaders := map[string]string{
+ "Content-Type": "image/svg+xml",
+ "Cache-Control": "public, max-age=12345",
+ }
+
+ expected, err := svg.Sanitize(&imagedata.ImageData{
+ Data: s.readTestFile("test1.svg"),
+ Headers: originHeaders,
+ })
+
+ require.Nil(s.T(), err)
+
+ require.True(s.T(), bytes.Equal(expected.Data, actual))
+ require.Equal(s.T(), originHeaders, expected.Headers)
+}
+
func (s *ProcessingHandlerTestSuite) TestNotSkipProcessingSVGToJPG() {
rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.svg@jpg")
res := rw.Result()
diff --git a/svg/svg.go b/svg/svg.go
index 8e51d0b83d..7e3d3e053d 100644
--- a/svg/svg.go
+++ b/svg/svg.go
@@ -27,7 +27,7 @@ const feDropShadowTemplate = `
`
-func Satitize(data *imagedata.ImageData) (*imagedata.ImageData, error) {
+func Sanitize(data *imagedata.ImageData) (*imagedata.ImageData, error) {
r := bytes.NewReader(data.Data)
l := xml.NewLexer(parse.NewInput(r))
@@ -62,8 +62,9 @@ func Satitize(data *imagedata.ImageData) (*imagedata.ImageData, error) {
}
newData := imagedata.ImageData{
- Data: buf.Bytes(),
- Type: data.Type,
+ Data: buf.Bytes(),
+ Type: data.Type,
+ Headers: data.Headers,
}
newData.SetCancel(cancel)
From 5f3d551f25935092c2c5b091342512a57ed8f16b Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 4 Jul 2023 18:30:26 +0300
Subject: [PATCH 032/138] Preserve headers in svg.FixUnsupported
---
svg/svg.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/svg/svg.go b/svg/svg.go
index 7e3d3e053d..e6ddc37310 100644
--- a/svg/svg.go
+++ b/svg/svg.go
@@ -194,8 +194,9 @@ func FixUnsupported(data *imagedata.ImageData) (*imagedata.ImageData, bool, erro
}
newData := imagedata.ImageData{
- Data: buf.Bytes(),
- Type: data.Type,
+ Data: buf.Bytes(),
+ Type: data.Type,
+ Headers: data.Headers,
}
newData.SetCancel(cancel)
From 93063787b1fcfe323a413086792bb506bb0f6db0 Mon Sep 17 00:00:00 2001
From: DarthSim
Date: Tue, 4 Jul 2023 18:31:06 +0300
Subject: [PATCH 033/138] Add svg package tests
---
processing_handler_test.go | 23 --------
svg/svg.go | 22 ++++----
svg/svg_test.go | 83 ++++++++++++++++++++++++++++
testdata/test1.drop-shadow.fixed.svg | 17 ++++++
testdata/test1.drop-shadow.svg | 9 +++
testdata/test1.sanitized.svg | 4 ++
testdata/test1.svg | 9 ++-
7 files changed, 131 insertions(+), 36 deletions(-)
create mode 100644 svg/svg_test.go
create mode 100644 testdata/test1.drop-shadow.fixed.svg
create mode 100644 testdata/test1.drop-shadow.svg
create mode 100644 testdata/test1.sanitized.svg
diff --git a/processing_handler_test.go b/processing_handler_test.go
index f5051e9077..449e032009 100644
--- a/processing_handler_test.go
+++ b/processing_handler_test.go
@@ -324,29 +324,6 @@ func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() {
require.True(s.T(), bytes.Equal(expected.Data, actual))
}
-func (s *ProcessingHandlerTestSuite) TestPreserveOriginSVGHeaders() {
- rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.svg")
- res := rw.Result()
-
- require.Equal(s.T(), 200, res.StatusCode)
-
- actual := s.readBody(res)
- originHeaders := map[string]string{
- "Content-Type": "image/svg+xml",
- "Cache-Control": "public, max-age=12345",
- }
-
- expected, err := svg.Sanitize(&imagedata.ImageData{
- Data: s.readTestFile("test1.svg"),
- Headers: originHeaders,
- })
-
- require.Nil(s.T(), err)
-
- require.True(s.T(), bytes.Equal(expected.Data, actual))
- require.Equal(s.T(), originHeaders, expected.Headers)
-}
-
func (s *ProcessingHandlerTestSuite) TestNotSkipProcessingSVGToJPG() {
rw := s.send("/unsafe/rs:fill:4:4/plain/local:///test1.svg@jpg")
res := rw.Result()
diff --git a/svg/svg.go b/svg/svg.go
index e6ddc37310..6bead71f59 100644
--- a/svg/svg.go
+++ b/svg/svg.go
@@ -15,17 +15,17 @@ import (
var feDropShadowName = []byte("feDropShadow")
-const feDropShadowTemplate = `
-
-
-
-
-
-
-
-
-
-`
+var feDropShadowTemplate = strings.TrimSpace(`
+
+
+
+
+
+
+
+
+
+`)
func Sanitize(data *imagedata.ImageData) (*imagedata.ImageData, error) {
r := bytes.NewReader(data.Data)
diff --git a/svg/svg_test.go b/svg/svg_test.go
new file mode 100644
index 0000000000..8d72c72587
--- /dev/null
+++ b/svg/svg_test.go
@@ -0,0 +1,83 @@
+package svg
+
+import (
+ "os"
+ "path/filepath"
+ "regexp"
+ "testing"
+
+ "github.com/imgproxy/imgproxy/v3/config"
+ "github.com/imgproxy/imgproxy/v3/imagedata"
+ "github.com/imgproxy/imgproxy/v3/imagetype"
+ "github.com/stretchr/testify/require"
+ "github.com/stretchr/testify/suite"
+)
+
+type SvgTestSuite struct {
+ suite.Suite
+}
+
+func (s *SvgTestSuite) SetupSuite() {
+ config.Reset()
+
+ err := imagedata.Init()
+ require.Nil(s.T(), err)
+}
+
+func (s *SvgTestSuite) readTestFile(name string) *imagedata.ImageData {
+ wd, err := os.Getwd()
+ require.Nil(s.T(), err)
+
+ data, err := os.ReadFile(filepath.Join(wd, "..", "testdata", name))
+ require.Nil(s.T(), err)
+
+ return &imagedata.ImageData{
+ Type: imagetype.SVG,
+ Data: data,
+ Headers: map[string]string{
+ "Content-Type": "image/svg+xml",
+ "Cache-Control": "public, max-age=12345",
+ },
+ }
+}
+
+func (s *SvgTestSuite) TestSanitize() {
+ origin := s.readTestFile("test1.svg")
+ expected := s.readTestFile("test1.sanitized.svg")
+
+ actual, err := Sanitize(origin)
+
+ require.Nil(s.T(), err)
+ require.Equal(s.T(), string(expected.Data), string(actual.Data))
+ require.Equal(s.T(), origin.Headers, actual.Headers)
+}
+
+func (s *SvgTestSuite) TestFixUnsupportedDropShadow() {
+ origin := s.readTestFile("test1.drop-shadow.svg")
+ expected := s.readTestFile("test1.drop-shadow.fixed.svg")
+
+ actual, changed, err := FixUnsupported(origin)
+
+ // `FixUnsupported` generates random IDs, we need to replace them for the test
+ re := regexp.MustCompile(`"ds(in|of)-.+?"`)
+ actualData := re.ReplaceAllString(string(actual.Data), `"ds$1-test"`)
+
+ require.Nil(s.T(), err)
+ require.True(s.T(), changed)
+ require.Equal(s.T(), string(expected.Data), actualData)
+ require.Equal(s.T(), origin.Headers, actual.Headers)
+}
+
+func (s *SvgTestSuite) TestFixUnsupportedNothingChanged() {
+ origin := s.readTestFile("test1.svg")
+
+ actual, changed, err := FixUnsupported(origin)
+
+ require.Nil(s.T(), err)
+ require.False(s.T(), changed)
+ require.Equal(s.T(), origin, actual)
+}
+
+func TestSvg(t *testing.T) {
+ suite.Run(t, new(SvgTestSuite))
+}
diff --git a/testdata/test1.drop-shadow.fixed.svg b/testdata/test1.drop-shadow.fixed.svg
new file mode 100644
index 0000000000..ef53c4cdf5
--- /dev/null
+++ b/testdata/test1.drop-shadow.fixed.svg
@@ -0,0 +1,17 @@
+
diff --git a/testdata/test1.drop-shadow.svg b/testdata/test1.drop-shadow.svg
new file mode 100644
index 0000000000..1f5764dbbd
--- /dev/null
+++ b/testdata/test1.drop-shadow.svg
@@ -0,0 +1,9 @@
+
diff --git a/testdata/test1.sanitized.svg b/testdata/test1.sanitized.svg
new file mode 100644
index 0000000000..9da5c05389
--- /dev/null
+++ b/testdata/test1.sanitized.svg
@@ -0,0 +1,4 @@
+
diff --git a/testdata/test1.svg b/testdata/test1.svg
index 0e203a57cd..e32f2db523 100644
--- a/testdata/test1.svg
+++ b/testdata/test1.svg
@@ -1,3 +1,8 @@
-
"+Ae(e.message+"",!0)+"
";throw e}},on.Parser=nn,on.parser=nn.parse,on.Renderer=Je,on.TextRenderer=Ke,on.Lexer=We,on.lexer=We.lex,on.Tokenizer=Ue,on.Slugger=en,on.parse=on;var rn={},cn=/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;function fn(e){return e.toLowerCase()}function pn(e){if("string"!=typeof e)return"";var n=e.trim().replace(/[A-Z]+/g,fn).replace(/<[^>]+>/g,"").replace(cn,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),e=rn[n],e=c.call(rn,n)?e+1:0;return n=(rn[n]=e)?n+"-"+e:n}pn.clear=function(){rn={}};var dn={baseURL:"https://github.githubassets.com/images/icons/emoji/",data:{100:"unicode/1f4af.png?v8",1234:"unicode/1f522.png?v8","+1":"unicode/1f44d.png?v8","-1":"unicode/1f44e.png?v8","1st_place_medal":"unicode/1f947.png?v8","2nd_place_medal":"unicode/1f948.png?v8","3rd_place_medal":"unicode/1f949.png?v8","8ball":"unicode/1f3b1.png?v8",a:"unicode/1f170.png?v8",ab:"unicode/1f18e.png?v8",abacus:"unicode/1f9ee.png?v8",abc:"unicode/1f524.png?v8",abcd:"unicode/1f521.png?v8",accept:"unicode/1f251.png?v8",accessibility:"accessibility.png?v8",accordion:"unicode/1fa97.png?v8",adhesive_bandage:"unicode/1fa79.png?v8",adult:"unicode/1f9d1.png?v8",aerial_tramway:"unicode/1f6a1.png?v8",afghanistan:"unicode/1f1e6-1f1eb.png?v8",airplane:"unicode/2708.png?v8",aland_islands:"unicode/1f1e6-1f1fd.png?v8",alarm_clock:"unicode/23f0.png?v8",albania:"unicode/1f1e6-1f1f1.png?v8",alembic:"unicode/2697.png?v8",algeria:"unicode/1f1e9-1f1ff.png?v8",alien:"unicode/1f47d.png?v8",ambulance:"unicode/1f691.png?v8",american_samoa:"unicode/1f1e6-1f1f8.png?v8",amphora:"unicode/1f3fa.png?v8",anatomical_heart:"unicode/1fac0.png?v8",anchor:"unicode/2693.png?v8",andorra:"unicode/1f1e6-1f1e9.png?v8",angel:"unicode/1f47c.png?v8",anger:"unicode/1f4a2.png?v8",angola:"unicode/1f1e6-1f1f4.png?v8",angry:"unicode/1f620.png?v8",anguilla:"unicode/1f1e6-1f1ee.png?v8",anguished:"unicode/1f627.png?v8",ant:"unicode/1f41c.png?v8",antarctica:"unicode/1f1e6-1f1f6.png?v8",antigua_barbuda:"unicode/1f1e6-1f1ec.png?v8",apple:"unicode/1f34e.png?v8",aquarius:"unicode/2652.png?v8",argentina:"unicode/1f1e6-1f1f7.png?v8",aries:"unicode/2648.png?v8",armenia:"unicode/1f1e6-1f1f2.png?v8",arrow_backward:"unicode/25c0.png?v8",arrow_double_down:"unicode/23ec.png?v8",arrow_double_up:"unicode/23eb.png?v8",arrow_down:"unicode/2b07.png?v8",arrow_down_small:"unicode/1f53d.png?v8",arrow_forward:"unicode/25b6.png?v8",arrow_heading_down:"unicode/2935.png?v8",arrow_heading_up:"unicode/2934.png?v8",arrow_left:"unicode/2b05.png?v8",arrow_lower_left:"unicode/2199.png?v8",arrow_lower_right:"unicode/2198.png?v8",arrow_right:"unicode/27a1.png?v8",arrow_right_hook:"unicode/21aa.png?v8",arrow_up:"unicode/2b06.png?v8",arrow_up_down:"unicode/2195.png?v8",arrow_up_small:"unicode/1f53c.png?v8",arrow_upper_left:"unicode/2196.png?v8",arrow_upper_right:"unicode/2197.png?v8",arrows_clockwise:"unicode/1f503.png?v8",arrows_counterclockwise:"unicode/1f504.png?v8",art:"unicode/1f3a8.png?v8",articulated_lorry:"unicode/1f69b.png?v8",artificial_satellite:"unicode/1f6f0.png?v8",artist:"unicode/1f9d1-1f3a8.png?v8",aruba:"unicode/1f1e6-1f1fc.png?v8",ascension_island:"unicode/1f1e6-1f1e8.png?v8",asterisk:"unicode/002a-20e3.png?v8",astonished:"unicode/1f632.png?v8",astronaut:"unicode/1f9d1-1f680.png?v8",athletic_shoe:"unicode/1f45f.png?v8",atm:"unicode/1f3e7.png?v8",atom:"atom.png?v8",atom_symbol:"unicode/269b.png?v8",australia:"unicode/1f1e6-1f1fa.png?v8",austria:"unicode/1f1e6-1f1f9.png?v8",auto_rickshaw:"unicode/1f6fa.png?v8",avocado:"unicode/1f951.png?v8",axe:"unicode/1fa93.png?v8",azerbaijan:"unicode/1f1e6-1f1ff.png?v8",b:"unicode/1f171.png?v8",baby:"unicode/1f476.png?v8",baby_bottle:"unicode/1f37c.png?v8",baby_chick:"unicode/1f424.png?v8",baby_symbol:"unicode/1f6bc.png?v8",back:"unicode/1f519.png?v8",bacon:"unicode/1f953.png?v8",badger:"unicode/1f9a1.png?v8",badminton:"unicode/1f3f8.png?v8",bagel:"unicode/1f96f.png?v8",baggage_claim:"unicode/1f6c4.png?v8",baguette_bread:"unicode/1f956.png?v8",bahamas:"unicode/1f1e7-1f1f8.png?v8",bahrain:"unicode/1f1e7-1f1ed.png?v8",balance_scale:"unicode/2696.png?v8",bald_man:"unicode/1f468-1f9b2.png?v8",bald_woman:"unicode/1f469-1f9b2.png?v8",ballet_shoes:"unicode/1fa70.png?v8",balloon:"unicode/1f388.png?v8",ballot_box:"unicode/1f5f3.png?v8",ballot_box_with_check:"unicode/2611.png?v8",bamboo:"unicode/1f38d.png?v8",banana:"unicode/1f34c.png?v8",bangbang:"unicode/203c.png?v8",bangladesh:"unicode/1f1e7-1f1e9.png?v8",banjo:"unicode/1fa95.png?v8",bank:"unicode/1f3e6.png?v8",bar_chart:"unicode/1f4ca.png?v8",barbados:"unicode/1f1e7-1f1e7.png?v8",barber:"unicode/1f488.png?v8",baseball:"unicode/26be.png?v8",basecamp:"basecamp.png?v8",basecampy:"basecampy.png?v8",basket:"unicode/1f9fa.png?v8",basketball:"unicode/1f3c0.png?v8",basketball_man:"unicode/26f9-2642.png?v8",basketball_woman:"unicode/26f9-2640.png?v8",bat:"unicode/1f987.png?v8",bath:"unicode/1f6c0.png?v8",bathtub:"unicode/1f6c1.png?v8",battery:"unicode/1f50b.png?v8",beach_umbrella:"unicode/1f3d6.png?v8",bear:"unicode/1f43b.png?v8",bearded_person:"unicode/1f9d4.png?v8",beaver:"unicode/1f9ab.png?v8",bed:"unicode/1f6cf.png?v8",bee:"unicode/1f41d.png?v8",beer:"unicode/1f37a.png?v8",beers:"unicode/1f37b.png?v8",beetle:"unicode/1fab2.png?v8",beginner:"unicode/1f530.png?v8",belarus:"unicode/1f1e7-1f1fe.png?v8",belgium:"unicode/1f1e7-1f1ea.png?v8",belize:"unicode/1f1e7-1f1ff.png?v8",bell:"unicode/1f514.png?v8",bell_pepper:"unicode/1fad1.png?v8",bellhop_bell:"unicode/1f6ce.png?v8",benin:"unicode/1f1e7-1f1ef.png?v8",bento:"unicode/1f371.png?v8",bermuda:"unicode/1f1e7-1f1f2.png?v8",beverage_box:"unicode/1f9c3.png?v8",bhutan:"unicode/1f1e7-1f1f9.png?v8",bicyclist:"unicode/1f6b4.png?v8",bike:"unicode/1f6b2.png?v8",biking_man:"unicode/1f6b4-2642.png?v8",biking_woman:"unicode/1f6b4-2640.png?v8",bikini:"unicode/1f459.png?v8",billed_cap:"unicode/1f9e2.png?v8",biohazard:"unicode/2623.png?v8",bird:"unicode/1f426.png?v8",birthday:"unicode/1f382.png?v8",bison:"unicode/1f9ac.png?v8",black_cat:"unicode/1f408-2b1b.png?v8",black_circle:"unicode/26ab.png?v8",black_flag:"unicode/1f3f4.png?v8",black_heart:"unicode/1f5a4.png?v8",black_joker:"unicode/1f0cf.png?v8",black_large_square:"unicode/2b1b.png?v8",black_medium_small_square:"unicode/25fe.png?v8",black_medium_square:"unicode/25fc.png?v8",black_nib:"unicode/2712.png?v8",black_small_square:"unicode/25aa.png?v8",black_square_button:"unicode/1f532.png?v8",blond_haired_man:"unicode/1f471-2642.png?v8",blond_haired_person:"unicode/1f471.png?v8",blond_haired_woman:"unicode/1f471-2640.png?v8",blonde_woman:"unicode/1f471-2640.png?v8",blossom:"unicode/1f33c.png?v8",blowfish:"unicode/1f421.png?v8",blue_book:"unicode/1f4d8.png?v8",blue_car:"unicode/1f699.png?v8",blue_heart:"unicode/1f499.png?v8",blue_square:"unicode/1f7e6.png?v8",blueberries:"unicode/1fad0.png?v8",blush:"unicode/1f60a.png?v8",boar:"unicode/1f417.png?v8",boat:"unicode/26f5.png?v8",bolivia:"unicode/1f1e7-1f1f4.png?v8",bomb:"unicode/1f4a3.png?v8",bone:"unicode/1f9b4.png?v8",book:"unicode/1f4d6.png?v8",bookmark:"unicode/1f516.png?v8",bookmark_tabs:"unicode/1f4d1.png?v8",books:"unicode/1f4da.png?v8",boom:"unicode/1f4a5.png?v8",boomerang:"unicode/1fa83.png?v8",boot:"unicode/1f462.png?v8",bosnia_herzegovina:"unicode/1f1e7-1f1e6.png?v8",botswana:"unicode/1f1e7-1f1fc.png?v8",bouncing_ball_man:"unicode/26f9-2642.png?v8",bouncing_ball_person:"unicode/26f9.png?v8",bouncing_ball_woman:"unicode/26f9-2640.png?v8",bouquet:"unicode/1f490.png?v8",bouvet_island:"unicode/1f1e7-1f1fb.png?v8",bow:"unicode/1f647.png?v8",bow_and_arrow:"unicode/1f3f9.png?v8",bowing_man:"unicode/1f647-2642.png?v8",bowing_woman:"unicode/1f647-2640.png?v8",bowl_with_spoon:"unicode/1f963.png?v8",bowling:"unicode/1f3b3.png?v8",bowtie:"bowtie.png?v8",boxing_glove:"unicode/1f94a.png?v8",boy:"unicode/1f466.png?v8",brain:"unicode/1f9e0.png?v8",brazil:"unicode/1f1e7-1f1f7.png?v8",bread:"unicode/1f35e.png?v8",breast_feeding:"unicode/1f931.png?v8",bricks:"unicode/1f9f1.png?v8",bride_with_veil:"unicode/1f470-2640.png?v8",bridge_at_night:"unicode/1f309.png?v8",briefcase:"unicode/1f4bc.png?v8",british_indian_ocean_territory:"unicode/1f1ee-1f1f4.png?v8",british_virgin_islands:"unicode/1f1fb-1f1ec.png?v8",broccoli:"unicode/1f966.png?v8",broken_heart:"unicode/1f494.png?v8",broom:"unicode/1f9f9.png?v8",brown_circle:"unicode/1f7e4.png?v8",brown_heart:"unicode/1f90e.png?v8",brown_square:"unicode/1f7eb.png?v8",brunei:"unicode/1f1e7-1f1f3.png?v8",bubble_tea:"unicode/1f9cb.png?v8",bucket:"unicode/1faa3.png?v8",bug:"unicode/1f41b.png?v8",building_construction:"unicode/1f3d7.png?v8",bulb:"unicode/1f4a1.png?v8",bulgaria:"unicode/1f1e7-1f1ec.png?v8",bullettrain_front:"unicode/1f685.png?v8",bullettrain_side:"unicode/1f684.png?v8",burkina_faso:"unicode/1f1e7-1f1eb.png?v8",burrito:"unicode/1f32f.png?v8",burundi:"unicode/1f1e7-1f1ee.png?v8",bus:"unicode/1f68c.png?v8",business_suit_levitating:"unicode/1f574.png?v8",busstop:"unicode/1f68f.png?v8",bust_in_silhouette:"unicode/1f464.png?v8",busts_in_silhouette:"unicode/1f465.png?v8",butter:"unicode/1f9c8.png?v8",butterfly:"unicode/1f98b.png?v8",cactus:"unicode/1f335.png?v8",cake:"unicode/1f370.png?v8",calendar:"unicode/1f4c6.png?v8",call_me_hand:"unicode/1f919.png?v8",calling:"unicode/1f4f2.png?v8",cambodia:"unicode/1f1f0-1f1ed.png?v8",camel:"unicode/1f42b.png?v8",camera:"unicode/1f4f7.png?v8",camera_flash:"unicode/1f4f8.png?v8",cameroon:"unicode/1f1e8-1f1f2.png?v8",camping:"unicode/1f3d5.png?v8",canada:"unicode/1f1e8-1f1e6.png?v8",canary_islands:"unicode/1f1ee-1f1e8.png?v8",cancer:"unicode/264b.png?v8",candle:"unicode/1f56f.png?v8",candy:"unicode/1f36c.png?v8",canned_food:"unicode/1f96b.png?v8",canoe:"unicode/1f6f6.png?v8",cape_verde:"unicode/1f1e8-1f1fb.png?v8",capital_abcd:"unicode/1f520.png?v8",capricorn:"unicode/2651.png?v8",car:"unicode/1f697.png?v8",card_file_box:"unicode/1f5c3.png?v8",card_index:"unicode/1f4c7.png?v8",card_index_dividers:"unicode/1f5c2.png?v8",caribbean_netherlands:"unicode/1f1e7-1f1f6.png?v8",carousel_horse:"unicode/1f3a0.png?v8",carpentry_saw:"unicode/1fa9a.png?v8",carrot:"unicode/1f955.png?v8",cartwheeling:"unicode/1f938.png?v8",cat:"unicode/1f431.png?v8",cat2:"unicode/1f408.png?v8",cayman_islands:"unicode/1f1f0-1f1fe.png?v8",cd:"unicode/1f4bf.png?v8",central_african_republic:"unicode/1f1e8-1f1eb.png?v8",ceuta_melilla:"unicode/1f1ea-1f1e6.png?v8",chad:"unicode/1f1f9-1f1e9.png?v8",chains:"unicode/26d3.png?v8",chair:"unicode/1fa91.png?v8",champagne:"unicode/1f37e.png?v8",chart:"unicode/1f4b9.png?v8",chart_with_downwards_trend:"unicode/1f4c9.png?v8",chart_with_upwards_trend:"unicode/1f4c8.png?v8",checkered_flag:"unicode/1f3c1.png?v8",cheese:"unicode/1f9c0.png?v8",cherries:"unicode/1f352.png?v8",cherry_blossom:"unicode/1f338.png?v8",chess_pawn:"unicode/265f.png?v8",chestnut:"unicode/1f330.png?v8",chicken:"unicode/1f414.png?v8",child:"unicode/1f9d2.png?v8",children_crossing:"unicode/1f6b8.png?v8",chile:"unicode/1f1e8-1f1f1.png?v8",chipmunk:"unicode/1f43f.png?v8",chocolate_bar:"unicode/1f36b.png?v8",chopsticks:"unicode/1f962.png?v8",christmas_island:"unicode/1f1e8-1f1fd.png?v8",christmas_tree:"unicode/1f384.png?v8",church:"unicode/26ea.png?v8",cinema:"unicode/1f3a6.png?v8",circus_tent:"unicode/1f3aa.png?v8",city_sunrise:"unicode/1f307.png?v8",city_sunset:"unicode/1f306.png?v8",cityscape:"unicode/1f3d9.png?v8",cl:"unicode/1f191.png?v8",clamp:"unicode/1f5dc.png?v8",clap:"unicode/1f44f.png?v8",clapper:"unicode/1f3ac.png?v8",classical_building:"unicode/1f3db.png?v8",climbing:"unicode/1f9d7.png?v8",climbing_man:"unicode/1f9d7-2642.png?v8",climbing_woman:"unicode/1f9d7-2640.png?v8",clinking_glasses:"unicode/1f942.png?v8",clipboard:"unicode/1f4cb.png?v8",clipperton_island:"unicode/1f1e8-1f1f5.png?v8",clock1:"unicode/1f550.png?v8",clock10:"unicode/1f559.png?v8",clock1030:"unicode/1f565.png?v8",clock11:"unicode/1f55a.png?v8",clock1130:"unicode/1f566.png?v8",clock12:"unicode/1f55b.png?v8",clock1230:"unicode/1f567.png?v8",clock130:"unicode/1f55c.png?v8",clock2:"unicode/1f551.png?v8",clock230:"unicode/1f55d.png?v8",clock3:"unicode/1f552.png?v8",clock330:"unicode/1f55e.png?v8",clock4:"unicode/1f553.png?v8",clock430:"unicode/1f55f.png?v8",clock5:"unicode/1f554.png?v8",clock530:"unicode/1f560.png?v8",clock6:"unicode/1f555.png?v8",clock630:"unicode/1f561.png?v8",clock7:"unicode/1f556.png?v8",clock730:"unicode/1f562.png?v8",clock8:"unicode/1f557.png?v8",clock830:"unicode/1f563.png?v8",clock9:"unicode/1f558.png?v8",clock930:"unicode/1f564.png?v8",closed_book:"unicode/1f4d5.png?v8",closed_lock_with_key:"unicode/1f510.png?v8",closed_umbrella:"unicode/1f302.png?v8",cloud:"unicode/2601.png?v8",cloud_with_lightning:"unicode/1f329.png?v8",cloud_with_lightning_and_rain:"unicode/26c8.png?v8",cloud_with_rain:"unicode/1f327.png?v8",cloud_with_snow:"unicode/1f328.png?v8",clown_face:"unicode/1f921.png?v8",clubs:"unicode/2663.png?v8",cn:"unicode/1f1e8-1f1f3.png?v8",coat:"unicode/1f9e5.png?v8",cockroach:"unicode/1fab3.png?v8",cocktail:"unicode/1f378.png?v8",coconut:"unicode/1f965.png?v8",cocos_islands:"unicode/1f1e8-1f1e8.png?v8",coffee:"unicode/2615.png?v8",coffin:"unicode/26b0.png?v8",coin:"unicode/1fa99.png?v8",cold_face:"unicode/1f976.png?v8",cold_sweat:"unicode/1f630.png?v8",collision:"unicode/1f4a5.png?v8",colombia:"unicode/1f1e8-1f1f4.png?v8",comet:"unicode/2604.png?v8",comoros:"unicode/1f1f0-1f1f2.png?v8",compass:"unicode/1f9ed.png?v8",computer:"unicode/1f4bb.png?v8",computer_mouse:"unicode/1f5b1.png?v8",confetti_ball:"unicode/1f38a.png?v8",confounded:"unicode/1f616.png?v8",confused:"unicode/1f615.png?v8",congo_brazzaville:"unicode/1f1e8-1f1ec.png?v8",congo_kinshasa:"unicode/1f1e8-1f1e9.png?v8",congratulations:"unicode/3297.png?v8",construction:"unicode/1f6a7.png?v8",construction_worker:"unicode/1f477.png?v8",construction_worker_man:"unicode/1f477-2642.png?v8",construction_worker_woman:"unicode/1f477-2640.png?v8",control_knobs:"unicode/1f39b.png?v8",convenience_store:"unicode/1f3ea.png?v8",cook:"unicode/1f9d1-1f373.png?v8",cook_islands:"unicode/1f1e8-1f1f0.png?v8",cookie:"unicode/1f36a.png?v8",cool:"unicode/1f192.png?v8",cop:"unicode/1f46e.png?v8",copyright:"unicode/00a9.png?v8",corn:"unicode/1f33d.png?v8",costa_rica:"unicode/1f1e8-1f1f7.png?v8",cote_divoire:"unicode/1f1e8-1f1ee.png?v8",couch_and_lamp:"unicode/1f6cb.png?v8",couple:"unicode/1f46b.png?v8",couple_with_heart:"unicode/1f491.png?v8",couple_with_heart_man_man:"unicode/1f468-2764-1f468.png?v8",couple_with_heart_woman_man:"unicode/1f469-2764-1f468.png?v8",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469.png?v8",couplekiss:"unicode/1f48f.png?v8",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468.png?v8",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468.png?v8",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469.png?v8",cow:"unicode/1f42e.png?v8",cow2:"unicode/1f404.png?v8",cowboy_hat_face:"unicode/1f920.png?v8",crab:"unicode/1f980.png?v8",crayon:"unicode/1f58d.png?v8",credit_card:"unicode/1f4b3.png?v8",crescent_moon:"unicode/1f319.png?v8",cricket:"unicode/1f997.png?v8",cricket_game:"unicode/1f3cf.png?v8",croatia:"unicode/1f1ed-1f1f7.png?v8",crocodile:"unicode/1f40a.png?v8",croissant:"unicode/1f950.png?v8",crossed_fingers:"unicode/1f91e.png?v8",crossed_flags:"unicode/1f38c.png?v8",crossed_swords:"unicode/2694.png?v8",crown:"unicode/1f451.png?v8",cry:"unicode/1f622.png?v8",crying_cat_face:"unicode/1f63f.png?v8",crystal_ball:"unicode/1f52e.png?v8",cuba:"unicode/1f1e8-1f1fa.png?v8",cucumber:"unicode/1f952.png?v8",cup_with_straw:"unicode/1f964.png?v8",cupcake:"unicode/1f9c1.png?v8",cupid:"unicode/1f498.png?v8",curacao:"unicode/1f1e8-1f1fc.png?v8",curling_stone:"unicode/1f94c.png?v8",curly_haired_man:"unicode/1f468-1f9b1.png?v8",curly_haired_woman:"unicode/1f469-1f9b1.png?v8",curly_loop:"unicode/27b0.png?v8",currency_exchange:"unicode/1f4b1.png?v8",curry:"unicode/1f35b.png?v8",cursing_face:"unicode/1f92c.png?v8",custard:"unicode/1f36e.png?v8",customs:"unicode/1f6c3.png?v8",cut_of_meat:"unicode/1f969.png?v8",cyclone:"unicode/1f300.png?v8",cyprus:"unicode/1f1e8-1f1fe.png?v8",czech_republic:"unicode/1f1e8-1f1ff.png?v8",dagger:"unicode/1f5e1.png?v8",dancer:"unicode/1f483.png?v8",dancers:"unicode/1f46f.png?v8",dancing_men:"unicode/1f46f-2642.png?v8",dancing_women:"unicode/1f46f-2640.png?v8",dango:"unicode/1f361.png?v8",dark_sunglasses:"unicode/1f576.png?v8",dart:"unicode/1f3af.png?v8",dash:"unicode/1f4a8.png?v8",date:"unicode/1f4c5.png?v8",de:"unicode/1f1e9-1f1ea.png?v8",deaf_man:"unicode/1f9cf-2642.png?v8",deaf_person:"unicode/1f9cf.png?v8",deaf_woman:"unicode/1f9cf-2640.png?v8",deciduous_tree:"unicode/1f333.png?v8",deer:"unicode/1f98c.png?v8",denmark:"unicode/1f1e9-1f1f0.png?v8",department_store:"unicode/1f3ec.png?v8",dependabot:"dependabot.png?v8",derelict_house:"unicode/1f3da.png?v8",desert:"unicode/1f3dc.png?v8",desert_island:"unicode/1f3dd.png?v8",desktop_computer:"unicode/1f5a5.png?v8",detective:"unicode/1f575.png?v8",diamond_shape_with_a_dot_inside:"unicode/1f4a0.png?v8",diamonds:"unicode/2666.png?v8",diego_garcia:"unicode/1f1e9-1f1ec.png?v8",disappointed:"unicode/1f61e.png?v8",disappointed_relieved:"unicode/1f625.png?v8",disguised_face:"unicode/1f978.png?v8",diving_mask:"unicode/1f93f.png?v8",diya_lamp:"unicode/1fa94.png?v8",dizzy:"unicode/1f4ab.png?v8",dizzy_face:"unicode/1f635.png?v8",djibouti:"unicode/1f1e9-1f1ef.png?v8",dna:"unicode/1f9ec.png?v8",do_not_litter:"unicode/1f6af.png?v8",dodo:"unicode/1f9a4.png?v8",dog:"unicode/1f436.png?v8",dog2:"unicode/1f415.png?v8",dollar:"unicode/1f4b5.png?v8",dolls:"unicode/1f38e.png?v8",dolphin:"unicode/1f42c.png?v8",dominica:"unicode/1f1e9-1f1f2.png?v8",dominican_republic:"unicode/1f1e9-1f1f4.png?v8",door:"unicode/1f6aa.png?v8",doughnut:"unicode/1f369.png?v8",dove:"unicode/1f54a.png?v8",dragon:"unicode/1f409.png?v8",dragon_face:"unicode/1f432.png?v8",dress:"unicode/1f457.png?v8",dromedary_camel:"unicode/1f42a.png?v8",drooling_face:"unicode/1f924.png?v8",drop_of_blood:"unicode/1fa78.png?v8",droplet:"unicode/1f4a7.png?v8",drum:"unicode/1f941.png?v8",duck:"unicode/1f986.png?v8",dumpling:"unicode/1f95f.png?v8",dvd:"unicode/1f4c0.png?v8","e-mail":"unicode/1f4e7.png?v8",eagle:"unicode/1f985.png?v8",ear:"unicode/1f442.png?v8",ear_of_rice:"unicode/1f33e.png?v8",ear_with_hearing_aid:"unicode/1f9bb.png?v8",earth_africa:"unicode/1f30d.png?v8",earth_americas:"unicode/1f30e.png?v8",earth_asia:"unicode/1f30f.png?v8",ecuador:"unicode/1f1ea-1f1e8.png?v8",egg:"unicode/1f95a.png?v8",eggplant:"unicode/1f346.png?v8",egypt:"unicode/1f1ea-1f1ec.png?v8",eight:"unicode/0038-20e3.png?v8",eight_pointed_black_star:"unicode/2734.png?v8",eight_spoked_asterisk:"unicode/2733.png?v8",eject_button:"unicode/23cf.png?v8",el_salvador:"unicode/1f1f8-1f1fb.png?v8",electric_plug:"unicode/1f50c.png?v8",electron:"electron.png?v8",elephant:"unicode/1f418.png?v8",elevator:"unicode/1f6d7.png?v8",elf:"unicode/1f9dd.png?v8",elf_man:"unicode/1f9dd-2642.png?v8",elf_woman:"unicode/1f9dd-2640.png?v8",email:"unicode/1f4e7.png?v8",end:"unicode/1f51a.png?v8",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png?v8",envelope:"unicode/2709.png?v8",envelope_with_arrow:"unicode/1f4e9.png?v8",equatorial_guinea:"unicode/1f1ec-1f1f6.png?v8",eritrea:"unicode/1f1ea-1f1f7.png?v8",es:"unicode/1f1ea-1f1f8.png?v8",estonia:"unicode/1f1ea-1f1ea.png?v8",ethiopia:"unicode/1f1ea-1f1f9.png?v8",eu:"unicode/1f1ea-1f1fa.png?v8",euro:"unicode/1f4b6.png?v8",european_castle:"unicode/1f3f0.png?v8",european_post_office:"unicode/1f3e4.png?v8",european_union:"unicode/1f1ea-1f1fa.png?v8",evergreen_tree:"unicode/1f332.png?v8",exclamation:"unicode/2757.png?v8",exploding_head:"unicode/1f92f.png?v8",expressionless:"unicode/1f611.png?v8",eye:"unicode/1f441.png?v8",eye_speech_bubble:"unicode/1f441-1f5e8.png?v8",eyeglasses:"unicode/1f453.png?v8",eyes:"unicode/1f440.png?v8",face_exhaling:"unicode/1f62e-1f4a8.png?v8",face_in_clouds:"unicode/1f636-1f32b.png?v8",face_with_head_bandage:"unicode/1f915.png?v8",face_with_spiral_eyes:"unicode/1f635-1f4ab.png?v8",face_with_thermometer:"unicode/1f912.png?v8",facepalm:"unicode/1f926.png?v8",facepunch:"unicode/1f44a.png?v8",factory:"unicode/1f3ed.png?v8",factory_worker:"unicode/1f9d1-1f3ed.png?v8",fairy:"unicode/1f9da.png?v8",fairy_man:"unicode/1f9da-2642.png?v8",fairy_woman:"unicode/1f9da-2640.png?v8",falafel:"unicode/1f9c6.png?v8",falkland_islands:"unicode/1f1eb-1f1f0.png?v8",fallen_leaf:"unicode/1f342.png?v8",family:"unicode/1f46a.png?v8",family_man_boy:"unicode/1f468-1f466.png?v8",family_man_boy_boy:"unicode/1f468-1f466-1f466.png?v8",family_man_girl:"unicode/1f468-1f467.png?v8",family_man_girl_boy:"unicode/1f468-1f467-1f466.png?v8",family_man_girl_girl:"unicode/1f468-1f467-1f467.png?v8",family_man_man_boy:"unicode/1f468-1f468-1f466.png?v8",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466.png?v8",family_man_man_girl:"unicode/1f468-1f468-1f467.png?v8",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466.png?v8",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467.png?v8",family_man_woman_boy:"unicode/1f468-1f469-1f466.png?v8",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466.png?v8",family_man_woman_girl:"unicode/1f468-1f469-1f467.png?v8",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466.png?v8",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467.png?v8",family_woman_boy:"unicode/1f469-1f466.png?v8",family_woman_boy_boy:"unicode/1f469-1f466-1f466.png?v8",family_woman_girl:"unicode/1f469-1f467.png?v8",family_woman_girl_boy:"unicode/1f469-1f467-1f466.png?v8",family_woman_girl_girl:"unicode/1f469-1f467-1f467.png?v8",family_woman_woman_boy:"unicode/1f469-1f469-1f466.png?v8",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466.png?v8",family_woman_woman_girl:"unicode/1f469-1f469-1f467.png?v8",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466.png?v8",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467.png?v8",farmer:"unicode/1f9d1-1f33e.png?v8",faroe_islands:"unicode/1f1eb-1f1f4.png?v8",fast_forward:"unicode/23e9.png?v8",fax:"unicode/1f4e0.png?v8",fearful:"unicode/1f628.png?v8",feather:"unicode/1fab6.png?v8",feelsgood:"feelsgood.png?v8",feet:"unicode/1f43e.png?v8",female_detective:"unicode/1f575-2640.png?v8",female_sign:"unicode/2640.png?v8",ferris_wheel:"unicode/1f3a1.png?v8",ferry:"unicode/26f4.png?v8",field_hockey:"unicode/1f3d1.png?v8",fiji:"unicode/1f1eb-1f1ef.png?v8",file_cabinet:"unicode/1f5c4.png?v8",file_folder:"unicode/1f4c1.png?v8",film_projector:"unicode/1f4fd.png?v8",film_strip:"unicode/1f39e.png?v8",finland:"unicode/1f1eb-1f1ee.png?v8",finnadie:"finnadie.png?v8",fire:"unicode/1f525.png?v8",fire_engine:"unicode/1f692.png?v8",fire_extinguisher:"unicode/1f9ef.png?v8",firecracker:"unicode/1f9e8.png?v8",firefighter:"unicode/1f9d1-1f692.png?v8",fireworks:"unicode/1f386.png?v8",first_quarter_moon:"unicode/1f313.png?v8",first_quarter_moon_with_face:"unicode/1f31b.png?v8",fish:"unicode/1f41f.png?v8",fish_cake:"unicode/1f365.png?v8",fishing_pole_and_fish:"unicode/1f3a3.png?v8",fishsticks:"fishsticks.png?v8",fist:"unicode/270a.png?v8",fist_left:"unicode/1f91b.png?v8",fist_oncoming:"unicode/1f44a.png?v8",fist_raised:"unicode/270a.png?v8",fist_right:"unicode/1f91c.png?v8",five:"unicode/0035-20e3.png?v8",flags:"unicode/1f38f.png?v8",flamingo:"unicode/1f9a9.png?v8",flashlight:"unicode/1f526.png?v8",flat_shoe:"unicode/1f97f.png?v8",flatbread:"unicode/1fad3.png?v8",fleur_de_lis:"unicode/269c.png?v8",flight_arrival:"unicode/1f6ec.png?v8",flight_departure:"unicode/1f6eb.png?v8",flipper:"unicode/1f42c.png?v8",floppy_disk:"unicode/1f4be.png?v8",flower_playing_cards:"unicode/1f3b4.png?v8",flushed:"unicode/1f633.png?v8",fly:"unicode/1fab0.png?v8",flying_disc:"unicode/1f94f.png?v8",flying_saucer:"unicode/1f6f8.png?v8",fog:"unicode/1f32b.png?v8",foggy:"unicode/1f301.png?v8",fondue:"unicode/1fad5.png?v8",foot:"unicode/1f9b6.png?v8",football:"unicode/1f3c8.png?v8",footprints:"unicode/1f463.png?v8",fork_and_knife:"unicode/1f374.png?v8",fortune_cookie:"unicode/1f960.png?v8",fountain:"unicode/26f2.png?v8",fountain_pen:"unicode/1f58b.png?v8",four:"unicode/0034-20e3.png?v8",four_leaf_clover:"unicode/1f340.png?v8",fox_face:"unicode/1f98a.png?v8",fr:"unicode/1f1eb-1f1f7.png?v8",framed_picture:"unicode/1f5bc.png?v8",free:"unicode/1f193.png?v8",french_guiana:"unicode/1f1ec-1f1eb.png?v8",french_polynesia:"unicode/1f1f5-1f1eb.png?v8",french_southern_territories:"unicode/1f1f9-1f1eb.png?v8",fried_egg:"unicode/1f373.png?v8",fried_shrimp:"unicode/1f364.png?v8",fries:"unicode/1f35f.png?v8",frog:"unicode/1f438.png?v8",frowning:"unicode/1f626.png?v8",frowning_face:"unicode/2639.png?v8",frowning_man:"unicode/1f64d-2642.png?v8",frowning_person:"unicode/1f64d.png?v8",frowning_woman:"unicode/1f64d-2640.png?v8",fu:"unicode/1f595.png?v8",fuelpump:"unicode/26fd.png?v8",full_moon:"unicode/1f315.png?v8",full_moon_with_face:"unicode/1f31d.png?v8",funeral_urn:"unicode/26b1.png?v8",gabon:"unicode/1f1ec-1f1e6.png?v8",gambia:"unicode/1f1ec-1f1f2.png?v8",game_die:"unicode/1f3b2.png?v8",garlic:"unicode/1f9c4.png?v8",gb:"unicode/1f1ec-1f1e7.png?v8",gear:"unicode/2699.png?v8",gem:"unicode/1f48e.png?v8",gemini:"unicode/264a.png?v8",genie:"unicode/1f9de.png?v8",genie_man:"unicode/1f9de-2642.png?v8",genie_woman:"unicode/1f9de-2640.png?v8",georgia:"unicode/1f1ec-1f1ea.png?v8",ghana:"unicode/1f1ec-1f1ed.png?v8",ghost:"unicode/1f47b.png?v8",gibraltar:"unicode/1f1ec-1f1ee.png?v8",gift:"unicode/1f381.png?v8",gift_heart:"unicode/1f49d.png?v8",giraffe:"unicode/1f992.png?v8",girl:"unicode/1f467.png?v8",globe_with_meridians:"unicode/1f310.png?v8",gloves:"unicode/1f9e4.png?v8",goal_net:"unicode/1f945.png?v8",goat:"unicode/1f410.png?v8",goberserk:"goberserk.png?v8",godmode:"godmode.png?v8",goggles:"unicode/1f97d.png?v8",golf:"unicode/26f3.png?v8",golfing:"unicode/1f3cc.png?v8",golfing_man:"unicode/1f3cc-2642.png?v8",golfing_woman:"unicode/1f3cc-2640.png?v8",gorilla:"unicode/1f98d.png?v8",grapes:"unicode/1f347.png?v8",greece:"unicode/1f1ec-1f1f7.png?v8",green_apple:"unicode/1f34f.png?v8",green_book:"unicode/1f4d7.png?v8",green_circle:"unicode/1f7e2.png?v8",green_heart:"unicode/1f49a.png?v8",green_salad:"unicode/1f957.png?v8",green_square:"unicode/1f7e9.png?v8",greenland:"unicode/1f1ec-1f1f1.png?v8",grenada:"unicode/1f1ec-1f1e9.png?v8",grey_exclamation:"unicode/2755.png?v8",grey_question:"unicode/2754.png?v8",grimacing:"unicode/1f62c.png?v8",grin:"unicode/1f601.png?v8",grinning:"unicode/1f600.png?v8",guadeloupe:"unicode/1f1ec-1f1f5.png?v8",guam:"unicode/1f1ec-1f1fa.png?v8",guard:"unicode/1f482.png?v8",guardsman:"unicode/1f482-2642.png?v8",guardswoman:"unicode/1f482-2640.png?v8",guatemala:"unicode/1f1ec-1f1f9.png?v8",guernsey:"unicode/1f1ec-1f1ec.png?v8",guide_dog:"unicode/1f9ae.png?v8",guinea:"unicode/1f1ec-1f1f3.png?v8",guinea_bissau:"unicode/1f1ec-1f1fc.png?v8",guitar:"unicode/1f3b8.png?v8",gun:"unicode/1f52b.png?v8",guyana:"unicode/1f1ec-1f1fe.png?v8",haircut:"unicode/1f487.png?v8",haircut_man:"unicode/1f487-2642.png?v8",haircut_woman:"unicode/1f487-2640.png?v8",haiti:"unicode/1f1ed-1f1f9.png?v8",hamburger:"unicode/1f354.png?v8",hammer:"unicode/1f528.png?v8",hammer_and_pick:"unicode/2692.png?v8",hammer_and_wrench:"unicode/1f6e0.png?v8",hamster:"unicode/1f439.png?v8",hand:"unicode/270b.png?v8",hand_over_mouth:"unicode/1f92d.png?v8",handbag:"unicode/1f45c.png?v8",handball_person:"unicode/1f93e.png?v8",handshake:"unicode/1f91d.png?v8",hankey:"unicode/1f4a9.png?v8",hash:"unicode/0023-20e3.png?v8",hatched_chick:"unicode/1f425.png?v8",hatching_chick:"unicode/1f423.png?v8",headphones:"unicode/1f3a7.png?v8",headstone:"unicode/1faa6.png?v8",health_worker:"unicode/1f9d1-2695.png?v8",hear_no_evil:"unicode/1f649.png?v8",heard_mcdonald_islands:"unicode/1f1ed-1f1f2.png?v8",heart:"unicode/2764.png?v8",heart_decoration:"unicode/1f49f.png?v8",heart_eyes:"unicode/1f60d.png?v8",heart_eyes_cat:"unicode/1f63b.png?v8",heart_on_fire:"unicode/2764-1f525.png?v8",heartbeat:"unicode/1f493.png?v8",heartpulse:"unicode/1f497.png?v8",hearts:"unicode/2665.png?v8",heavy_check_mark:"unicode/2714.png?v8",heavy_division_sign:"unicode/2797.png?v8",heavy_dollar_sign:"unicode/1f4b2.png?v8",heavy_exclamation_mark:"unicode/2757.png?v8",heavy_heart_exclamation:"unicode/2763.png?v8",heavy_minus_sign:"unicode/2796.png?v8",heavy_multiplication_x:"unicode/2716.png?v8",heavy_plus_sign:"unicode/2795.png?v8",hedgehog:"unicode/1f994.png?v8",helicopter:"unicode/1f681.png?v8",herb:"unicode/1f33f.png?v8",hibiscus:"unicode/1f33a.png?v8",high_brightness:"unicode/1f506.png?v8",high_heel:"unicode/1f460.png?v8",hiking_boot:"unicode/1f97e.png?v8",hindu_temple:"unicode/1f6d5.png?v8",hippopotamus:"unicode/1f99b.png?v8",hocho:"unicode/1f52a.png?v8",hole:"unicode/1f573.png?v8",honduras:"unicode/1f1ed-1f1f3.png?v8",honey_pot:"unicode/1f36f.png?v8",honeybee:"unicode/1f41d.png?v8",hong_kong:"unicode/1f1ed-1f1f0.png?v8",hook:"unicode/1fa9d.png?v8",horse:"unicode/1f434.png?v8",horse_racing:"unicode/1f3c7.png?v8",hospital:"unicode/1f3e5.png?v8",hot_face:"unicode/1f975.png?v8",hot_pepper:"unicode/1f336.png?v8",hotdog:"unicode/1f32d.png?v8",hotel:"unicode/1f3e8.png?v8",hotsprings:"unicode/2668.png?v8",hourglass:"unicode/231b.png?v8",hourglass_flowing_sand:"unicode/23f3.png?v8",house:"unicode/1f3e0.png?v8",house_with_garden:"unicode/1f3e1.png?v8",houses:"unicode/1f3d8.png?v8",hugs:"unicode/1f917.png?v8",hungary:"unicode/1f1ed-1f1fa.png?v8",hurtrealbad:"hurtrealbad.png?v8",hushed:"unicode/1f62f.png?v8",hut:"unicode/1f6d6.png?v8",ice_cream:"unicode/1f368.png?v8",ice_cube:"unicode/1f9ca.png?v8",ice_hockey:"unicode/1f3d2.png?v8",ice_skate:"unicode/26f8.png?v8",icecream:"unicode/1f366.png?v8",iceland:"unicode/1f1ee-1f1f8.png?v8",id:"unicode/1f194.png?v8",ideograph_advantage:"unicode/1f250.png?v8",imp:"unicode/1f47f.png?v8",inbox_tray:"unicode/1f4e5.png?v8",incoming_envelope:"unicode/1f4e8.png?v8",india:"unicode/1f1ee-1f1f3.png?v8",indonesia:"unicode/1f1ee-1f1e9.png?v8",infinity:"unicode/267e.png?v8",information_desk_person:"unicode/1f481.png?v8",information_source:"unicode/2139.png?v8",innocent:"unicode/1f607.png?v8",interrobang:"unicode/2049.png?v8",iphone:"unicode/1f4f1.png?v8",iran:"unicode/1f1ee-1f1f7.png?v8",iraq:"unicode/1f1ee-1f1f6.png?v8",ireland:"unicode/1f1ee-1f1ea.png?v8",isle_of_man:"unicode/1f1ee-1f1f2.png?v8",israel:"unicode/1f1ee-1f1f1.png?v8",it:"unicode/1f1ee-1f1f9.png?v8",izakaya_lantern:"unicode/1f3ee.png?v8",jack_o_lantern:"unicode/1f383.png?v8",jamaica:"unicode/1f1ef-1f1f2.png?v8",japan:"unicode/1f5fe.png?v8",japanese_castle:"unicode/1f3ef.png?v8",japanese_goblin:"unicode/1f47a.png?v8",japanese_ogre:"unicode/1f479.png?v8",jeans:"unicode/1f456.png?v8",jersey:"unicode/1f1ef-1f1ea.png?v8",jigsaw:"unicode/1f9e9.png?v8",jordan:"unicode/1f1ef-1f1f4.png?v8",joy:"unicode/1f602.png?v8",joy_cat:"unicode/1f639.png?v8",joystick:"unicode/1f579.png?v8",jp:"unicode/1f1ef-1f1f5.png?v8",judge:"unicode/1f9d1-2696.png?v8",juggling_person:"unicode/1f939.png?v8",kaaba:"unicode/1f54b.png?v8",kangaroo:"unicode/1f998.png?v8",kazakhstan:"unicode/1f1f0-1f1ff.png?v8",kenya:"unicode/1f1f0-1f1ea.png?v8",key:"unicode/1f511.png?v8",keyboard:"unicode/2328.png?v8",keycap_ten:"unicode/1f51f.png?v8",kick_scooter:"unicode/1f6f4.png?v8",kimono:"unicode/1f458.png?v8",kiribati:"unicode/1f1f0-1f1ee.png?v8",kiss:"unicode/1f48b.png?v8",kissing:"unicode/1f617.png?v8",kissing_cat:"unicode/1f63d.png?v8",kissing_closed_eyes:"unicode/1f61a.png?v8",kissing_heart:"unicode/1f618.png?v8",kissing_smiling_eyes:"unicode/1f619.png?v8",kite:"unicode/1fa81.png?v8",kiwi_fruit:"unicode/1f95d.png?v8",kneeling_man:"unicode/1f9ce-2642.png?v8",kneeling_person:"unicode/1f9ce.png?v8",kneeling_woman:"unicode/1f9ce-2640.png?v8",knife:"unicode/1f52a.png?v8",knot:"unicode/1faa2.png?v8",koala:"unicode/1f428.png?v8",koko:"unicode/1f201.png?v8",kosovo:"unicode/1f1fd-1f1f0.png?v8",kr:"unicode/1f1f0-1f1f7.png?v8",kuwait:"unicode/1f1f0-1f1fc.png?v8",kyrgyzstan:"unicode/1f1f0-1f1ec.png?v8",lab_coat:"unicode/1f97c.png?v8",label:"unicode/1f3f7.png?v8",lacrosse:"unicode/1f94d.png?v8",ladder:"unicode/1fa9c.png?v8",lady_beetle:"unicode/1f41e.png?v8",lantern:"unicode/1f3ee.png?v8",laos:"unicode/1f1f1-1f1e6.png?v8",large_blue_circle:"unicode/1f535.png?v8",large_blue_diamond:"unicode/1f537.png?v8",large_orange_diamond:"unicode/1f536.png?v8",last_quarter_moon:"unicode/1f317.png?v8",last_quarter_moon_with_face:"unicode/1f31c.png?v8",latin_cross:"unicode/271d.png?v8",latvia:"unicode/1f1f1-1f1fb.png?v8",laughing:"unicode/1f606.png?v8",leafy_green:"unicode/1f96c.png?v8",leaves:"unicode/1f343.png?v8",lebanon:"unicode/1f1f1-1f1e7.png?v8",ledger:"unicode/1f4d2.png?v8",left_luggage:"unicode/1f6c5.png?v8",left_right_arrow:"unicode/2194.png?v8",left_speech_bubble:"unicode/1f5e8.png?v8",leftwards_arrow_with_hook:"unicode/21a9.png?v8",leg:"unicode/1f9b5.png?v8",lemon:"unicode/1f34b.png?v8",leo:"unicode/264c.png?v8",leopard:"unicode/1f406.png?v8",lesotho:"unicode/1f1f1-1f1f8.png?v8",level_slider:"unicode/1f39a.png?v8",liberia:"unicode/1f1f1-1f1f7.png?v8",libra:"unicode/264e.png?v8",libya:"unicode/1f1f1-1f1fe.png?v8",liechtenstein:"unicode/1f1f1-1f1ee.png?v8",light_rail:"unicode/1f688.png?v8",link:"unicode/1f517.png?v8",lion:"unicode/1f981.png?v8",lips:"unicode/1f444.png?v8",lipstick:"unicode/1f484.png?v8",lithuania:"unicode/1f1f1-1f1f9.png?v8",lizard:"unicode/1f98e.png?v8",llama:"unicode/1f999.png?v8",lobster:"unicode/1f99e.png?v8",lock:"unicode/1f512.png?v8",lock_with_ink_pen:"unicode/1f50f.png?v8",lollipop:"unicode/1f36d.png?v8",long_drum:"unicode/1fa98.png?v8",loop:"unicode/27bf.png?v8",lotion_bottle:"unicode/1f9f4.png?v8",lotus_position:"unicode/1f9d8.png?v8",lotus_position_man:"unicode/1f9d8-2642.png?v8",lotus_position_woman:"unicode/1f9d8-2640.png?v8",loud_sound:"unicode/1f50a.png?v8",loudspeaker:"unicode/1f4e2.png?v8",love_hotel:"unicode/1f3e9.png?v8",love_letter:"unicode/1f48c.png?v8",love_you_gesture:"unicode/1f91f.png?v8",low_brightness:"unicode/1f505.png?v8",luggage:"unicode/1f9f3.png?v8",lungs:"unicode/1fac1.png?v8",luxembourg:"unicode/1f1f1-1f1fa.png?v8",lying_face:"unicode/1f925.png?v8",m:"unicode/24c2.png?v8",macau:"unicode/1f1f2-1f1f4.png?v8",macedonia:"unicode/1f1f2-1f1f0.png?v8",madagascar:"unicode/1f1f2-1f1ec.png?v8",mag:"unicode/1f50d.png?v8",mag_right:"unicode/1f50e.png?v8",mage:"unicode/1f9d9.png?v8",mage_man:"unicode/1f9d9-2642.png?v8",mage_woman:"unicode/1f9d9-2640.png?v8",magic_wand:"unicode/1fa84.png?v8",magnet:"unicode/1f9f2.png?v8",mahjong:"unicode/1f004.png?v8",mailbox:"unicode/1f4eb.png?v8",mailbox_closed:"unicode/1f4ea.png?v8",mailbox_with_mail:"unicode/1f4ec.png?v8",mailbox_with_no_mail:"unicode/1f4ed.png?v8",malawi:"unicode/1f1f2-1f1fc.png?v8",malaysia:"unicode/1f1f2-1f1fe.png?v8",maldives:"unicode/1f1f2-1f1fb.png?v8",male_detective:"unicode/1f575-2642.png?v8",male_sign:"unicode/2642.png?v8",mali:"unicode/1f1f2-1f1f1.png?v8",malta:"unicode/1f1f2-1f1f9.png?v8",mammoth:"unicode/1f9a3.png?v8",man:"unicode/1f468.png?v8",man_artist:"unicode/1f468-1f3a8.png?v8",man_astronaut:"unicode/1f468-1f680.png?v8",man_beard:"unicode/1f9d4-2642.png?v8",man_cartwheeling:"unicode/1f938-2642.png?v8",man_cook:"unicode/1f468-1f373.png?v8",man_dancing:"unicode/1f57a.png?v8",man_facepalming:"unicode/1f926-2642.png?v8",man_factory_worker:"unicode/1f468-1f3ed.png?v8",man_farmer:"unicode/1f468-1f33e.png?v8",man_feeding_baby:"unicode/1f468-1f37c.png?v8",man_firefighter:"unicode/1f468-1f692.png?v8",man_health_worker:"unicode/1f468-2695.png?v8",man_in_manual_wheelchair:"unicode/1f468-1f9bd.png?v8",man_in_motorized_wheelchair:"unicode/1f468-1f9bc.png?v8",man_in_tuxedo:"unicode/1f935-2642.png?v8",man_judge:"unicode/1f468-2696.png?v8",man_juggling:"unicode/1f939-2642.png?v8",man_mechanic:"unicode/1f468-1f527.png?v8",man_office_worker:"unicode/1f468-1f4bc.png?v8",man_pilot:"unicode/1f468-2708.png?v8",man_playing_handball:"unicode/1f93e-2642.png?v8",man_playing_water_polo:"unicode/1f93d-2642.png?v8",man_scientist:"unicode/1f468-1f52c.png?v8",man_shrugging:"unicode/1f937-2642.png?v8",man_singer:"unicode/1f468-1f3a4.png?v8",man_student:"unicode/1f468-1f393.png?v8",man_teacher:"unicode/1f468-1f3eb.png?v8",man_technologist:"unicode/1f468-1f4bb.png?v8",man_with_gua_pi_mao:"unicode/1f472.png?v8",man_with_probing_cane:"unicode/1f468-1f9af.png?v8",man_with_turban:"unicode/1f473-2642.png?v8",man_with_veil:"unicode/1f470-2642.png?v8",mandarin:"unicode/1f34a.png?v8",mango:"unicode/1f96d.png?v8",mans_shoe:"unicode/1f45e.png?v8",mantelpiece_clock:"unicode/1f570.png?v8",manual_wheelchair:"unicode/1f9bd.png?v8",maple_leaf:"unicode/1f341.png?v8",marshall_islands:"unicode/1f1f2-1f1ed.png?v8",martial_arts_uniform:"unicode/1f94b.png?v8",martinique:"unicode/1f1f2-1f1f6.png?v8",mask:"unicode/1f637.png?v8",massage:"unicode/1f486.png?v8",massage_man:"unicode/1f486-2642.png?v8",massage_woman:"unicode/1f486-2640.png?v8",mate:"unicode/1f9c9.png?v8",mauritania:"unicode/1f1f2-1f1f7.png?v8",mauritius:"unicode/1f1f2-1f1fa.png?v8",mayotte:"unicode/1f1fe-1f1f9.png?v8",meat_on_bone:"unicode/1f356.png?v8",mechanic:"unicode/1f9d1-1f527.png?v8",mechanical_arm:"unicode/1f9be.png?v8",mechanical_leg:"unicode/1f9bf.png?v8",medal_military:"unicode/1f396.png?v8",medal_sports:"unicode/1f3c5.png?v8",medical_symbol:"unicode/2695.png?v8",mega:"unicode/1f4e3.png?v8",melon:"unicode/1f348.png?v8",memo:"unicode/1f4dd.png?v8",men_wrestling:"unicode/1f93c-2642.png?v8",mending_heart:"unicode/2764-1fa79.png?v8",menorah:"unicode/1f54e.png?v8",mens:"unicode/1f6b9.png?v8",mermaid:"unicode/1f9dc-2640.png?v8",merman:"unicode/1f9dc-2642.png?v8",merperson:"unicode/1f9dc.png?v8",metal:"unicode/1f918.png?v8",metro:"unicode/1f687.png?v8",mexico:"unicode/1f1f2-1f1fd.png?v8",microbe:"unicode/1f9a0.png?v8",micronesia:"unicode/1f1eb-1f1f2.png?v8",microphone:"unicode/1f3a4.png?v8",microscope:"unicode/1f52c.png?v8",middle_finger:"unicode/1f595.png?v8",military_helmet:"unicode/1fa96.png?v8",milk_glass:"unicode/1f95b.png?v8",milky_way:"unicode/1f30c.png?v8",minibus:"unicode/1f690.png?v8",minidisc:"unicode/1f4bd.png?v8",mirror:"unicode/1fa9e.png?v8",mobile_phone_off:"unicode/1f4f4.png?v8",moldova:"unicode/1f1f2-1f1e9.png?v8",monaco:"unicode/1f1f2-1f1e8.png?v8",money_mouth_face:"unicode/1f911.png?v8",money_with_wings:"unicode/1f4b8.png?v8",moneybag:"unicode/1f4b0.png?v8",mongolia:"unicode/1f1f2-1f1f3.png?v8",monkey:"unicode/1f412.png?v8",monkey_face:"unicode/1f435.png?v8",monocle_face:"unicode/1f9d0.png?v8",monorail:"unicode/1f69d.png?v8",montenegro:"unicode/1f1f2-1f1ea.png?v8",montserrat:"unicode/1f1f2-1f1f8.png?v8",moon:"unicode/1f314.png?v8",moon_cake:"unicode/1f96e.png?v8",morocco:"unicode/1f1f2-1f1e6.png?v8",mortar_board:"unicode/1f393.png?v8",mosque:"unicode/1f54c.png?v8",mosquito:"unicode/1f99f.png?v8",motor_boat:"unicode/1f6e5.png?v8",motor_scooter:"unicode/1f6f5.png?v8",motorcycle:"unicode/1f3cd.png?v8",motorized_wheelchair:"unicode/1f9bc.png?v8",motorway:"unicode/1f6e3.png?v8",mount_fuji:"unicode/1f5fb.png?v8",mountain:"unicode/26f0.png?v8",mountain_bicyclist:"unicode/1f6b5.png?v8",mountain_biking_man:"unicode/1f6b5-2642.png?v8",mountain_biking_woman:"unicode/1f6b5-2640.png?v8",mountain_cableway:"unicode/1f6a0.png?v8",mountain_railway:"unicode/1f69e.png?v8",mountain_snow:"unicode/1f3d4.png?v8",mouse:"unicode/1f42d.png?v8",mouse2:"unicode/1f401.png?v8",mouse_trap:"unicode/1faa4.png?v8",movie_camera:"unicode/1f3a5.png?v8",moyai:"unicode/1f5ff.png?v8",mozambique:"unicode/1f1f2-1f1ff.png?v8",mrs_claus:"unicode/1f936.png?v8",muscle:"unicode/1f4aa.png?v8",mushroom:"unicode/1f344.png?v8",musical_keyboard:"unicode/1f3b9.png?v8",musical_note:"unicode/1f3b5.png?v8",musical_score:"unicode/1f3bc.png?v8",mute:"unicode/1f507.png?v8",mx_claus:"unicode/1f9d1-1f384.png?v8",myanmar:"unicode/1f1f2-1f1f2.png?v8",nail_care:"unicode/1f485.png?v8",name_badge:"unicode/1f4db.png?v8",namibia:"unicode/1f1f3-1f1e6.png?v8",national_park:"unicode/1f3de.png?v8",nauru:"unicode/1f1f3-1f1f7.png?v8",nauseated_face:"unicode/1f922.png?v8",nazar_amulet:"unicode/1f9ff.png?v8",neckbeard:"neckbeard.png?v8",necktie:"unicode/1f454.png?v8",negative_squared_cross_mark:"unicode/274e.png?v8",nepal:"unicode/1f1f3-1f1f5.png?v8",nerd_face:"unicode/1f913.png?v8",nesting_dolls:"unicode/1fa86.png?v8",netherlands:"unicode/1f1f3-1f1f1.png?v8",neutral_face:"unicode/1f610.png?v8",new:"unicode/1f195.png?v8",new_caledonia:"unicode/1f1f3-1f1e8.png?v8",new_moon:"unicode/1f311.png?v8",new_moon_with_face:"unicode/1f31a.png?v8",new_zealand:"unicode/1f1f3-1f1ff.png?v8",newspaper:"unicode/1f4f0.png?v8",newspaper_roll:"unicode/1f5de.png?v8",next_track_button:"unicode/23ed.png?v8",ng:"unicode/1f196.png?v8",ng_man:"unicode/1f645-2642.png?v8",ng_woman:"unicode/1f645-2640.png?v8",nicaragua:"unicode/1f1f3-1f1ee.png?v8",niger:"unicode/1f1f3-1f1ea.png?v8",nigeria:"unicode/1f1f3-1f1ec.png?v8",night_with_stars:"unicode/1f303.png?v8",nine:"unicode/0039-20e3.png?v8",ninja:"unicode/1f977.png?v8",niue:"unicode/1f1f3-1f1fa.png?v8",no_bell:"unicode/1f515.png?v8",no_bicycles:"unicode/1f6b3.png?v8",no_entry:"unicode/26d4.png?v8",no_entry_sign:"unicode/1f6ab.png?v8",no_good:"unicode/1f645.png?v8",no_good_man:"unicode/1f645-2642.png?v8",no_good_woman:"unicode/1f645-2640.png?v8",no_mobile_phones:"unicode/1f4f5.png?v8",no_mouth:"unicode/1f636.png?v8",no_pedestrians:"unicode/1f6b7.png?v8",no_smoking:"unicode/1f6ad.png?v8","non-potable_water":"unicode/1f6b1.png?v8",norfolk_island:"unicode/1f1f3-1f1eb.png?v8",north_korea:"unicode/1f1f0-1f1f5.png?v8",northern_mariana_islands:"unicode/1f1f2-1f1f5.png?v8",norway:"unicode/1f1f3-1f1f4.png?v8",nose:"unicode/1f443.png?v8",notebook:"unicode/1f4d3.png?v8",notebook_with_decorative_cover:"unicode/1f4d4.png?v8",notes:"unicode/1f3b6.png?v8",nut_and_bolt:"unicode/1f529.png?v8",o:"unicode/2b55.png?v8",o2:"unicode/1f17e.png?v8",ocean:"unicode/1f30a.png?v8",octocat:"octocat.png?v8",octopus:"unicode/1f419.png?v8",oden:"unicode/1f362.png?v8",office:"unicode/1f3e2.png?v8",office_worker:"unicode/1f9d1-1f4bc.png?v8",oil_drum:"unicode/1f6e2.png?v8",ok:"unicode/1f197.png?v8",ok_hand:"unicode/1f44c.png?v8",ok_man:"unicode/1f646-2642.png?v8",ok_person:"unicode/1f646.png?v8",ok_woman:"unicode/1f646-2640.png?v8",old_key:"unicode/1f5dd.png?v8",older_adult:"unicode/1f9d3.png?v8",older_man:"unicode/1f474.png?v8",older_woman:"unicode/1f475.png?v8",olive:"unicode/1fad2.png?v8",om:"unicode/1f549.png?v8",oman:"unicode/1f1f4-1f1f2.png?v8",on:"unicode/1f51b.png?v8",oncoming_automobile:"unicode/1f698.png?v8",oncoming_bus:"unicode/1f68d.png?v8",oncoming_police_car:"unicode/1f694.png?v8",oncoming_taxi:"unicode/1f696.png?v8",one:"unicode/0031-20e3.png?v8",one_piece_swimsuit:"unicode/1fa71.png?v8",onion:"unicode/1f9c5.png?v8",open_book:"unicode/1f4d6.png?v8",open_file_folder:"unicode/1f4c2.png?v8",open_hands:"unicode/1f450.png?v8",open_mouth:"unicode/1f62e.png?v8",open_umbrella:"unicode/2602.png?v8",ophiuchus:"unicode/26ce.png?v8",orange:"unicode/1f34a.png?v8",orange_book:"unicode/1f4d9.png?v8",orange_circle:"unicode/1f7e0.png?v8",orange_heart:"unicode/1f9e1.png?v8",orange_square:"unicode/1f7e7.png?v8",orangutan:"unicode/1f9a7.png?v8",orthodox_cross:"unicode/2626.png?v8",otter:"unicode/1f9a6.png?v8",outbox_tray:"unicode/1f4e4.png?v8",owl:"unicode/1f989.png?v8",ox:"unicode/1f402.png?v8",oyster:"unicode/1f9aa.png?v8",package:"unicode/1f4e6.png?v8",page_facing_up:"unicode/1f4c4.png?v8",page_with_curl:"unicode/1f4c3.png?v8",pager:"unicode/1f4df.png?v8",paintbrush:"unicode/1f58c.png?v8",pakistan:"unicode/1f1f5-1f1f0.png?v8",palau:"unicode/1f1f5-1f1fc.png?v8",palestinian_territories:"unicode/1f1f5-1f1f8.png?v8",palm_tree:"unicode/1f334.png?v8",palms_up_together:"unicode/1f932.png?v8",panama:"unicode/1f1f5-1f1e6.png?v8",pancakes:"unicode/1f95e.png?v8",panda_face:"unicode/1f43c.png?v8",paperclip:"unicode/1f4ce.png?v8",paperclips:"unicode/1f587.png?v8",papua_new_guinea:"unicode/1f1f5-1f1ec.png?v8",parachute:"unicode/1fa82.png?v8",paraguay:"unicode/1f1f5-1f1fe.png?v8",parasol_on_ground:"unicode/26f1.png?v8",parking:"unicode/1f17f.png?v8",parrot:"unicode/1f99c.png?v8",part_alternation_mark:"unicode/303d.png?v8",partly_sunny:"unicode/26c5.png?v8",partying_face:"unicode/1f973.png?v8",passenger_ship:"unicode/1f6f3.png?v8",passport_control:"unicode/1f6c2.png?v8",pause_button:"unicode/23f8.png?v8",paw_prints:"unicode/1f43e.png?v8",peace_symbol:"unicode/262e.png?v8",peach:"unicode/1f351.png?v8",peacock:"unicode/1f99a.png?v8",peanuts:"unicode/1f95c.png?v8",pear:"unicode/1f350.png?v8",pen:"unicode/1f58a.png?v8",pencil:"unicode/1f4dd.png?v8",pencil2:"unicode/270f.png?v8",penguin:"unicode/1f427.png?v8",pensive:"unicode/1f614.png?v8",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1.png?v8",people_hugging:"unicode/1fac2.png?v8",performing_arts:"unicode/1f3ad.png?v8",persevere:"unicode/1f623.png?v8",person_bald:"unicode/1f9d1-1f9b2.png?v8",person_curly_hair:"unicode/1f9d1-1f9b1.png?v8",person_feeding_baby:"unicode/1f9d1-1f37c.png?v8",person_fencing:"unicode/1f93a.png?v8",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd.png?v8",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc.png?v8",person_in_tuxedo:"unicode/1f935.png?v8",person_red_hair:"unicode/1f9d1-1f9b0.png?v8",person_white_hair:"unicode/1f9d1-1f9b3.png?v8",person_with_probing_cane:"unicode/1f9d1-1f9af.png?v8",person_with_turban:"unicode/1f473.png?v8",person_with_veil:"unicode/1f470.png?v8",peru:"unicode/1f1f5-1f1ea.png?v8",petri_dish:"unicode/1f9eb.png?v8",philippines:"unicode/1f1f5-1f1ed.png?v8",phone:"unicode/260e.png?v8",pick:"unicode/26cf.png?v8",pickup_truck:"unicode/1f6fb.png?v8",pie:"unicode/1f967.png?v8",pig:"unicode/1f437.png?v8",pig2:"unicode/1f416.png?v8",pig_nose:"unicode/1f43d.png?v8",pill:"unicode/1f48a.png?v8",pilot:"unicode/1f9d1-2708.png?v8",pinata:"unicode/1fa85.png?v8",pinched_fingers:"unicode/1f90c.png?v8",pinching_hand:"unicode/1f90f.png?v8",pineapple:"unicode/1f34d.png?v8",ping_pong:"unicode/1f3d3.png?v8",pirate_flag:"unicode/1f3f4-2620.png?v8",pisces:"unicode/2653.png?v8",pitcairn_islands:"unicode/1f1f5-1f1f3.png?v8",pizza:"unicode/1f355.png?v8",placard:"unicode/1faa7.png?v8",place_of_worship:"unicode/1f6d0.png?v8",plate_with_cutlery:"unicode/1f37d.png?v8",play_or_pause_button:"unicode/23ef.png?v8",pleading_face:"unicode/1f97a.png?v8",plunger:"unicode/1faa0.png?v8",point_down:"unicode/1f447.png?v8",point_left:"unicode/1f448.png?v8",point_right:"unicode/1f449.png?v8",point_up:"unicode/261d.png?v8",point_up_2:"unicode/1f446.png?v8",poland:"unicode/1f1f5-1f1f1.png?v8",polar_bear:"unicode/1f43b-2744.png?v8",police_car:"unicode/1f693.png?v8",police_officer:"unicode/1f46e.png?v8",policeman:"unicode/1f46e-2642.png?v8",policewoman:"unicode/1f46e-2640.png?v8",poodle:"unicode/1f429.png?v8",poop:"unicode/1f4a9.png?v8",popcorn:"unicode/1f37f.png?v8",portugal:"unicode/1f1f5-1f1f9.png?v8",post_office:"unicode/1f3e3.png?v8",postal_horn:"unicode/1f4ef.png?v8",postbox:"unicode/1f4ee.png?v8",potable_water:"unicode/1f6b0.png?v8",potato:"unicode/1f954.png?v8",potted_plant:"unicode/1fab4.png?v8",pouch:"unicode/1f45d.png?v8",poultry_leg:"unicode/1f357.png?v8",pound:"unicode/1f4b7.png?v8",pout:"unicode/1f621.png?v8",pouting_cat:"unicode/1f63e.png?v8",pouting_face:"unicode/1f64e.png?v8",pouting_man:"unicode/1f64e-2642.png?v8",pouting_woman:"unicode/1f64e-2640.png?v8",pray:"unicode/1f64f.png?v8",prayer_beads:"unicode/1f4ff.png?v8",pregnant_woman:"unicode/1f930.png?v8",pretzel:"unicode/1f968.png?v8",previous_track_button:"unicode/23ee.png?v8",prince:"unicode/1f934.png?v8",princess:"unicode/1f478.png?v8",printer:"unicode/1f5a8.png?v8",probing_cane:"unicode/1f9af.png?v8",puerto_rico:"unicode/1f1f5-1f1f7.png?v8",punch:"unicode/1f44a.png?v8",purple_circle:"unicode/1f7e3.png?v8",purple_heart:"unicode/1f49c.png?v8",purple_square:"unicode/1f7ea.png?v8",purse:"unicode/1f45b.png?v8",pushpin:"unicode/1f4cc.png?v8",put_litter_in_its_place:"unicode/1f6ae.png?v8",qatar:"unicode/1f1f6-1f1e6.png?v8",question:"unicode/2753.png?v8",rabbit:"unicode/1f430.png?v8",rabbit2:"unicode/1f407.png?v8",raccoon:"unicode/1f99d.png?v8",racehorse:"unicode/1f40e.png?v8",racing_car:"unicode/1f3ce.png?v8",radio:"unicode/1f4fb.png?v8",radio_button:"unicode/1f518.png?v8",radioactive:"unicode/2622.png?v8",rage:"unicode/1f621.png?v8",rage1:"rage1.png?v8",rage2:"rage2.png?v8",rage3:"rage3.png?v8",rage4:"rage4.png?v8",railway_car:"unicode/1f683.png?v8",railway_track:"unicode/1f6e4.png?v8",rainbow:"unicode/1f308.png?v8",rainbow_flag:"unicode/1f3f3-1f308.png?v8",raised_back_of_hand:"unicode/1f91a.png?v8",raised_eyebrow:"unicode/1f928.png?v8",raised_hand:"unicode/270b.png?v8",raised_hand_with_fingers_splayed:"unicode/1f590.png?v8",raised_hands:"unicode/1f64c.png?v8",raising_hand:"unicode/1f64b.png?v8",raising_hand_man:"unicode/1f64b-2642.png?v8",raising_hand_woman:"unicode/1f64b-2640.png?v8",ram:"unicode/1f40f.png?v8",ramen:"unicode/1f35c.png?v8",rat:"unicode/1f400.png?v8",razor:"unicode/1fa92.png?v8",receipt:"unicode/1f9fe.png?v8",record_button:"unicode/23fa.png?v8",recycle:"unicode/267b.png?v8",red_car:"unicode/1f697.png?v8",red_circle:"unicode/1f534.png?v8",red_envelope:"unicode/1f9e7.png?v8",red_haired_man:"unicode/1f468-1f9b0.png?v8",red_haired_woman:"unicode/1f469-1f9b0.png?v8",red_square:"unicode/1f7e5.png?v8",registered:"unicode/00ae.png?v8",relaxed:"unicode/263a.png?v8",relieved:"unicode/1f60c.png?v8",reminder_ribbon:"unicode/1f397.png?v8",repeat:"unicode/1f501.png?v8",repeat_one:"unicode/1f502.png?v8",rescue_worker_helmet:"unicode/26d1.png?v8",restroom:"unicode/1f6bb.png?v8",reunion:"unicode/1f1f7-1f1ea.png?v8",revolving_hearts:"unicode/1f49e.png?v8",rewind:"unicode/23ea.png?v8",rhinoceros:"unicode/1f98f.png?v8",ribbon:"unicode/1f380.png?v8",rice:"unicode/1f35a.png?v8",rice_ball:"unicode/1f359.png?v8",rice_cracker:"unicode/1f358.png?v8",rice_scene:"unicode/1f391.png?v8",right_anger_bubble:"unicode/1f5ef.png?v8",ring:"unicode/1f48d.png?v8",ringed_planet:"unicode/1fa90.png?v8",robot:"unicode/1f916.png?v8",rock:"unicode/1faa8.png?v8",rocket:"unicode/1f680.png?v8",rofl:"unicode/1f923.png?v8",roll_eyes:"unicode/1f644.png?v8",roll_of_paper:"unicode/1f9fb.png?v8",roller_coaster:"unicode/1f3a2.png?v8",roller_skate:"unicode/1f6fc.png?v8",romania:"unicode/1f1f7-1f1f4.png?v8",rooster:"unicode/1f413.png?v8",rose:"unicode/1f339.png?v8",rosette:"unicode/1f3f5.png?v8",rotating_light:"unicode/1f6a8.png?v8",round_pushpin:"unicode/1f4cd.png?v8",rowboat:"unicode/1f6a3.png?v8",rowing_man:"unicode/1f6a3-2642.png?v8",rowing_woman:"unicode/1f6a3-2640.png?v8",ru:"unicode/1f1f7-1f1fa.png?v8",rugby_football:"unicode/1f3c9.png?v8",runner:"unicode/1f3c3.png?v8",running:"unicode/1f3c3.png?v8",running_man:"unicode/1f3c3-2642.png?v8",running_shirt_with_sash:"unicode/1f3bd.png?v8",running_woman:"unicode/1f3c3-2640.png?v8",rwanda:"unicode/1f1f7-1f1fc.png?v8",sa:"unicode/1f202.png?v8",safety_pin:"unicode/1f9f7.png?v8",safety_vest:"unicode/1f9ba.png?v8",sagittarius:"unicode/2650.png?v8",sailboat:"unicode/26f5.png?v8",sake:"unicode/1f376.png?v8",salt:"unicode/1f9c2.png?v8",samoa:"unicode/1f1fc-1f1f8.png?v8",san_marino:"unicode/1f1f8-1f1f2.png?v8",sandal:"unicode/1f461.png?v8",sandwich:"unicode/1f96a.png?v8",santa:"unicode/1f385.png?v8",sao_tome_principe:"unicode/1f1f8-1f1f9.png?v8",sari:"unicode/1f97b.png?v8",sassy_man:"unicode/1f481-2642.png?v8",sassy_woman:"unicode/1f481-2640.png?v8",satellite:"unicode/1f4e1.png?v8",satisfied:"unicode/1f606.png?v8",saudi_arabia:"unicode/1f1f8-1f1e6.png?v8",sauna_man:"unicode/1f9d6-2642.png?v8",sauna_person:"unicode/1f9d6.png?v8",sauna_woman:"unicode/1f9d6-2640.png?v8",sauropod:"unicode/1f995.png?v8",saxophone:"unicode/1f3b7.png?v8",scarf:"unicode/1f9e3.png?v8",school:"unicode/1f3eb.png?v8",school_satchel:"unicode/1f392.png?v8",scientist:"unicode/1f9d1-1f52c.png?v8",scissors:"unicode/2702.png?v8",scorpion:"unicode/1f982.png?v8",scorpius:"unicode/264f.png?v8",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png?v8",scream:"unicode/1f631.png?v8",scream_cat:"unicode/1f640.png?v8",screwdriver:"unicode/1fa9b.png?v8",scroll:"unicode/1f4dc.png?v8",seal:"unicode/1f9ad.png?v8",seat:"unicode/1f4ba.png?v8",secret:"unicode/3299.png?v8",see_no_evil:"unicode/1f648.png?v8",seedling:"unicode/1f331.png?v8",selfie:"unicode/1f933.png?v8",senegal:"unicode/1f1f8-1f1f3.png?v8",serbia:"unicode/1f1f7-1f1f8.png?v8",service_dog:"unicode/1f415-1f9ba.png?v8",seven:"unicode/0037-20e3.png?v8",sewing_needle:"unicode/1faa1.png?v8",seychelles:"unicode/1f1f8-1f1e8.png?v8",shallow_pan_of_food:"unicode/1f958.png?v8",shamrock:"unicode/2618.png?v8",shark:"unicode/1f988.png?v8",shaved_ice:"unicode/1f367.png?v8",sheep:"unicode/1f411.png?v8",shell:"unicode/1f41a.png?v8",shield:"unicode/1f6e1.png?v8",shinto_shrine:"unicode/26e9.png?v8",ship:"unicode/1f6a2.png?v8",shipit:"shipit.png?v8",shirt:"unicode/1f455.png?v8",shit:"unicode/1f4a9.png?v8",shoe:"unicode/1f45e.png?v8",shopping:"unicode/1f6cd.png?v8",shopping_cart:"unicode/1f6d2.png?v8",shorts:"unicode/1fa73.png?v8",shower:"unicode/1f6bf.png?v8",shrimp:"unicode/1f990.png?v8",shrug:"unicode/1f937.png?v8",shushing_face:"unicode/1f92b.png?v8",sierra_leone:"unicode/1f1f8-1f1f1.png?v8",signal_strength:"unicode/1f4f6.png?v8",singapore:"unicode/1f1f8-1f1ec.png?v8",singer:"unicode/1f9d1-1f3a4.png?v8",sint_maarten:"unicode/1f1f8-1f1fd.png?v8",six:"unicode/0036-20e3.png?v8",six_pointed_star:"unicode/1f52f.png?v8",skateboard:"unicode/1f6f9.png?v8",ski:"unicode/1f3bf.png?v8",skier:"unicode/26f7.png?v8",skull:"unicode/1f480.png?v8",skull_and_crossbones:"unicode/2620.png?v8",skunk:"unicode/1f9a8.png?v8",sled:"unicode/1f6f7.png?v8",sleeping:"unicode/1f634.png?v8",sleeping_bed:"unicode/1f6cc.png?v8",sleepy:"unicode/1f62a.png?v8",slightly_frowning_face:"unicode/1f641.png?v8",slightly_smiling_face:"unicode/1f642.png?v8",slot_machine:"unicode/1f3b0.png?v8",sloth:"unicode/1f9a5.png?v8",slovakia:"unicode/1f1f8-1f1f0.png?v8",slovenia:"unicode/1f1f8-1f1ee.png?v8",small_airplane:"unicode/1f6e9.png?v8",small_blue_diamond:"unicode/1f539.png?v8",small_orange_diamond:"unicode/1f538.png?v8",small_red_triangle:"unicode/1f53a.png?v8",small_red_triangle_down:"unicode/1f53b.png?v8",smile:"unicode/1f604.png?v8",smile_cat:"unicode/1f638.png?v8",smiley:"unicode/1f603.png?v8",smiley_cat:"unicode/1f63a.png?v8",smiling_face_with_tear:"unicode/1f972.png?v8",smiling_face_with_three_hearts:"unicode/1f970.png?v8",smiling_imp:"unicode/1f608.png?v8",smirk:"unicode/1f60f.png?v8",smirk_cat:"unicode/1f63c.png?v8",smoking:"unicode/1f6ac.png?v8",snail:"unicode/1f40c.png?v8",snake:"unicode/1f40d.png?v8",sneezing_face:"unicode/1f927.png?v8",snowboarder:"unicode/1f3c2.png?v8",snowflake:"unicode/2744.png?v8",snowman:"unicode/26c4.png?v8",snowman_with_snow:"unicode/2603.png?v8",soap:"unicode/1f9fc.png?v8",sob:"unicode/1f62d.png?v8",soccer:"unicode/26bd.png?v8",socks:"unicode/1f9e6.png?v8",softball:"unicode/1f94e.png?v8",solomon_islands:"unicode/1f1f8-1f1e7.png?v8",somalia:"unicode/1f1f8-1f1f4.png?v8",soon:"unicode/1f51c.png?v8",sos:"unicode/1f198.png?v8",sound:"unicode/1f509.png?v8",south_africa:"unicode/1f1ff-1f1e6.png?v8",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8.png?v8",south_sudan:"unicode/1f1f8-1f1f8.png?v8",space_invader:"unicode/1f47e.png?v8",spades:"unicode/2660.png?v8",spaghetti:"unicode/1f35d.png?v8",sparkle:"unicode/2747.png?v8",sparkler:"unicode/1f387.png?v8",sparkles:"unicode/2728.png?v8",sparkling_heart:"unicode/1f496.png?v8",speak_no_evil:"unicode/1f64a.png?v8",speaker:"unicode/1f508.png?v8",speaking_head:"unicode/1f5e3.png?v8",speech_balloon:"unicode/1f4ac.png?v8",speedboat:"unicode/1f6a4.png?v8",spider:"unicode/1f577.png?v8",spider_web:"unicode/1f578.png?v8",spiral_calendar:"unicode/1f5d3.png?v8",spiral_notepad:"unicode/1f5d2.png?v8",sponge:"unicode/1f9fd.png?v8",spoon:"unicode/1f944.png?v8",squid:"unicode/1f991.png?v8",sri_lanka:"unicode/1f1f1-1f1f0.png?v8",st_barthelemy:"unicode/1f1e7-1f1f1.png?v8",st_helena:"unicode/1f1f8-1f1ed.png?v8",st_kitts_nevis:"unicode/1f1f0-1f1f3.png?v8",st_lucia:"unicode/1f1f1-1f1e8.png?v8",st_martin:"unicode/1f1f2-1f1eb.png?v8",st_pierre_miquelon:"unicode/1f1f5-1f1f2.png?v8",st_vincent_grenadines:"unicode/1f1fb-1f1e8.png?v8",stadium:"unicode/1f3df.png?v8",standing_man:"unicode/1f9cd-2642.png?v8",standing_person:"unicode/1f9cd.png?v8",standing_woman:"unicode/1f9cd-2640.png?v8",star:"unicode/2b50.png?v8",star2:"unicode/1f31f.png?v8",star_and_crescent:"unicode/262a.png?v8",star_of_david:"unicode/2721.png?v8",star_struck:"unicode/1f929.png?v8",stars:"unicode/1f320.png?v8",station:"unicode/1f689.png?v8",statue_of_liberty:"unicode/1f5fd.png?v8",steam_locomotive:"unicode/1f682.png?v8",stethoscope:"unicode/1fa7a.png?v8",stew:"unicode/1f372.png?v8",stop_button:"unicode/23f9.png?v8",stop_sign:"unicode/1f6d1.png?v8",stopwatch:"unicode/23f1.png?v8",straight_ruler:"unicode/1f4cf.png?v8",strawberry:"unicode/1f353.png?v8",stuck_out_tongue:"unicode/1f61b.png?v8",stuck_out_tongue_closed_eyes:"unicode/1f61d.png?v8",stuck_out_tongue_winking_eye:"unicode/1f61c.png?v8",student:"unicode/1f9d1-1f393.png?v8",studio_microphone:"unicode/1f399.png?v8",stuffed_flatbread:"unicode/1f959.png?v8",sudan:"unicode/1f1f8-1f1e9.png?v8",sun_behind_large_cloud:"unicode/1f325.png?v8",sun_behind_rain_cloud:"unicode/1f326.png?v8",sun_behind_small_cloud:"unicode/1f324.png?v8",sun_with_face:"unicode/1f31e.png?v8",sunflower:"unicode/1f33b.png?v8",sunglasses:"unicode/1f60e.png?v8",sunny:"unicode/2600.png?v8",sunrise:"unicode/1f305.png?v8",sunrise_over_mountains:"unicode/1f304.png?v8",superhero:"unicode/1f9b8.png?v8",superhero_man:"unicode/1f9b8-2642.png?v8",superhero_woman:"unicode/1f9b8-2640.png?v8",supervillain:"unicode/1f9b9.png?v8",supervillain_man:"unicode/1f9b9-2642.png?v8",supervillain_woman:"unicode/1f9b9-2640.png?v8",surfer:"unicode/1f3c4.png?v8",surfing_man:"unicode/1f3c4-2642.png?v8",surfing_woman:"unicode/1f3c4-2640.png?v8",suriname:"unicode/1f1f8-1f1f7.png?v8",sushi:"unicode/1f363.png?v8",suspect:"suspect.png?v8",suspension_railway:"unicode/1f69f.png?v8",svalbard_jan_mayen:"unicode/1f1f8-1f1ef.png?v8",swan:"unicode/1f9a2.png?v8",swaziland:"unicode/1f1f8-1f1ff.png?v8",sweat:"unicode/1f613.png?v8",sweat_drops:"unicode/1f4a6.png?v8",sweat_smile:"unicode/1f605.png?v8",sweden:"unicode/1f1f8-1f1ea.png?v8",sweet_potato:"unicode/1f360.png?v8",swim_brief:"unicode/1fa72.png?v8",swimmer:"unicode/1f3ca.png?v8",swimming_man:"unicode/1f3ca-2642.png?v8",swimming_woman:"unicode/1f3ca-2640.png?v8",switzerland:"unicode/1f1e8-1f1ed.png?v8",symbols:"unicode/1f523.png?v8",synagogue:"unicode/1f54d.png?v8",syria:"unicode/1f1f8-1f1fe.png?v8",syringe:"unicode/1f489.png?v8","t-rex":"unicode/1f996.png?v8",taco:"unicode/1f32e.png?v8",tada:"unicode/1f389.png?v8",taiwan:"unicode/1f1f9-1f1fc.png?v8",tajikistan:"unicode/1f1f9-1f1ef.png?v8",takeout_box:"unicode/1f961.png?v8",tamale:"unicode/1fad4.png?v8",tanabata_tree:"unicode/1f38b.png?v8",tangerine:"unicode/1f34a.png?v8",tanzania:"unicode/1f1f9-1f1ff.png?v8",taurus:"unicode/2649.png?v8",taxi:"unicode/1f695.png?v8",tea:"unicode/1f375.png?v8",teacher:"unicode/1f9d1-1f3eb.png?v8",teapot:"unicode/1fad6.png?v8",technologist:"unicode/1f9d1-1f4bb.png?v8",teddy_bear:"unicode/1f9f8.png?v8",telephone:"unicode/260e.png?v8",telephone_receiver:"unicode/1f4de.png?v8",telescope:"unicode/1f52d.png?v8",tennis:"unicode/1f3be.png?v8",tent:"unicode/26fa.png?v8",test_tube:"unicode/1f9ea.png?v8",thailand:"unicode/1f1f9-1f1ed.png?v8",thermometer:"unicode/1f321.png?v8",thinking:"unicode/1f914.png?v8",thong_sandal:"unicode/1fa74.png?v8",thought_balloon:"unicode/1f4ad.png?v8",thread:"unicode/1f9f5.png?v8",three:"unicode/0033-20e3.png?v8",thumbsdown:"unicode/1f44e.png?v8",thumbsup:"unicode/1f44d.png?v8",ticket:"unicode/1f3ab.png?v8",tickets:"unicode/1f39f.png?v8",tiger:"unicode/1f42f.png?v8",tiger2:"unicode/1f405.png?v8",timer_clock:"unicode/23f2.png?v8",timor_leste:"unicode/1f1f9-1f1f1.png?v8",tipping_hand_man:"unicode/1f481-2642.png?v8",tipping_hand_person:"unicode/1f481.png?v8",tipping_hand_woman:"unicode/1f481-2640.png?v8",tired_face:"unicode/1f62b.png?v8",tm:"unicode/2122.png?v8",togo:"unicode/1f1f9-1f1ec.png?v8",toilet:"unicode/1f6bd.png?v8",tokelau:"unicode/1f1f9-1f1f0.png?v8",tokyo_tower:"unicode/1f5fc.png?v8",tomato:"unicode/1f345.png?v8",tonga:"unicode/1f1f9-1f1f4.png?v8",tongue:"unicode/1f445.png?v8",toolbox:"unicode/1f9f0.png?v8",tooth:"unicode/1f9b7.png?v8",toothbrush:"unicode/1faa5.png?v8",top:"unicode/1f51d.png?v8",tophat:"unicode/1f3a9.png?v8",tornado:"unicode/1f32a.png?v8",tr:"unicode/1f1f9-1f1f7.png?v8",trackball:"unicode/1f5b2.png?v8",tractor:"unicode/1f69c.png?v8",traffic_light:"unicode/1f6a5.png?v8",train:"unicode/1f68b.png?v8",train2:"unicode/1f686.png?v8",tram:"unicode/1f68a.png?v8",transgender_flag:"unicode/1f3f3-26a7.png?v8",transgender_symbol:"unicode/26a7.png?v8",triangular_flag_on_post:"unicode/1f6a9.png?v8",triangular_ruler:"unicode/1f4d0.png?v8",trident:"unicode/1f531.png?v8",trinidad_tobago:"unicode/1f1f9-1f1f9.png?v8",tristan_da_cunha:"unicode/1f1f9-1f1e6.png?v8",triumph:"unicode/1f624.png?v8",trolleybus:"unicode/1f68e.png?v8",trollface:"trollface.png?v8",trophy:"unicode/1f3c6.png?v8",tropical_drink:"unicode/1f379.png?v8",tropical_fish:"unicode/1f420.png?v8",truck:"unicode/1f69a.png?v8",trumpet:"unicode/1f3ba.png?v8",tshirt:"unicode/1f455.png?v8",tulip:"unicode/1f337.png?v8",tumbler_glass:"unicode/1f943.png?v8",tunisia:"unicode/1f1f9-1f1f3.png?v8",turkey:"unicode/1f983.png?v8",turkmenistan:"unicode/1f1f9-1f1f2.png?v8",turks_caicos_islands:"unicode/1f1f9-1f1e8.png?v8",turtle:"unicode/1f422.png?v8",tuvalu:"unicode/1f1f9-1f1fb.png?v8",tv:"unicode/1f4fa.png?v8",twisted_rightwards_arrows:"unicode/1f500.png?v8",two:"unicode/0032-20e3.png?v8",two_hearts:"unicode/1f495.png?v8",two_men_holding_hands:"unicode/1f46c.png?v8",two_women_holding_hands:"unicode/1f46d.png?v8",u5272:"unicode/1f239.png?v8",u5408:"unicode/1f234.png?v8",u55b6:"unicode/1f23a.png?v8",u6307:"unicode/1f22f.png?v8",u6708:"unicode/1f237.png?v8",u6709:"unicode/1f236.png?v8",u6e80:"unicode/1f235.png?v8",u7121:"unicode/1f21a.png?v8",u7533:"unicode/1f238.png?v8",u7981:"unicode/1f232.png?v8",u7a7a:"unicode/1f233.png?v8",uganda:"unicode/1f1fa-1f1ec.png?v8",uk:"unicode/1f1ec-1f1e7.png?v8",ukraine:"unicode/1f1fa-1f1e6.png?v8",umbrella:"unicode/2614.png?v8",unamused:"unicode/1f612.png?v8",underage:"unicode/1f51e.png?v8",unicorn:"unicode/1f984.png?v8",united_arab_emirates:"unicode/1f1e6-1f1ea.png?v8",united_nations:"unicode/1f1fa-1f1f3.png?v8",unlock:"unicode/1f513.png?v8",up:"unicode/1f199.png?v8",upside_down_face:"unicode/1f643.png?v8",uruguay:"unicode/1f1fa-1f1fe.png?v8",us:"unicode/1f1fa-1f1f8.png?v8",us_outlying_islands:"unicode/1f1fa-1f1f2.png?v8",us_virgin_islands:"unicode/1f1fb-1f1ee.png?v8",uzbekistan:"unicode/1f1fa-1f1ff.png?v8",v:"unicode/270c.png?v8",vampire:"unicode/1f9db.png?v8",vampire_man:"unicode/1f9db-2642.png?v8",vampire_woman:"unicode/1f9db-2640.png?v8",vanuatu:"unicode/1f1fb-1f1fa.png?v8",vatican_city:"unicode/1f1fb-1f1e6.png?v8",venezuela:"unicode/1f1fb-1f1ea.png?v8",vertical_traffic_light:"unicode/1f6a6.png?v8",vhs:"unicode/1f4fc.png?v8",vibration_mode:"unicode/1f4f3.png?v8",video_camera:"unicode/1f4f9.png?v8",video_game:"unicode/1f3ae.png?v8",vietnam:"unicode/1f1fb-1f1f3.png?v8",violin:"unicode/1f3bb.png?v8",virgo:"unicode/264d.png?v8",volcano:"unicode/1f30b.png?v8",volleyball:"unicode/1f3d0.png?v8",vomiting_face:"unicode/1f92e.png?v8",vs:"unicode/1f19a.png?v8",vulcan_salute:"unicode/1f596.png?v8",waffle:"unicode/1f9c7.png?v8",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png?v8",walking:"unicode/1f6b6.png?v8",walking_man:"unicode/1f6b6-2642.png?v8",walking_woman:"unicode/1f6b6-2640.png?v8",wallis_futuna:"unicode/1f1fc-1f1eb.png?v8",waning_crescent_moon:"unicode/1f318.png?v8",waning_gibbous_moon:"unicode/1f316.png?v8",warning:"unicode/26a0.png?v8",wastebasket:"unicode/1f5d1.png?v8",watch:"unicode/231a.png?v8",water_buffalo:"unicode/1f403.png?v8",water_polo:"unicode/1f93d.png?v8",watermelon:"unicode/1f349.png?v8",wave:"unicode/1f44b.png?v8",wavy_dash:"unicode/3030.png?v8",waxing_crescent_moon:"unicode/1f312.png?v8",waxing_gibbous_moon:"unicode/1f314.png?v8",wc:"unicode/1f6be.png?v8",weary:"unicode/1f629.png?v8",wedding:"unicode/1f492.png?v8",weight_lifting:"unicode/1f3cb.png?v8",weight_lifting_man:"unicode/1f3cb-2642.png?v8",weight_lifting_woman:"unicode/1f3cb-2640.png?v8",western_sahara:"unicode/1f1ea-1f1ed.png?v8",whale:"unicode/1f433.png?v8",whale2:"unicode/1f40b.png?v8",wheel_of_dharma:"unicode/2638.png?v8",wheelchair:"unicode/267f.png?v8",white_check_mark:"unicode/2705.png?v8",white_circle:"unicode/26aa.png?v8",white_flag:"unicode/1f3f3.png?v8",white_flower:"unicode/1f4ae.png?v8",white_haired_man:"unicode/1f468-1f9b3.png?v8",white_haired_woman:"unicode/1f469-1f9b3.png?v8",white_heart:"unicode/1f90d.png?v8",white_large_square:"unicode/2b1c.png?v8",white_medium_small_square:"unicode/25fd.png?v8",white_medium_square:"unicode/25fb.png?v8",white_small_square:"unicode/25ab.png?v8",white_square_button:"unicode/1f533.png?v8",wilted_flower:"unicode/1f940.png?v8",wind_chime:"unicode/1f390.png?v8",wind_face:"unicode/1f32c.png?v8",window:"unicode/1fa9f.png?v8",wine_glass:"unicode/1f377.png?v8",wink:"unicode/1f609.png?v8",wolf:"unicode/1f43a.png?v8",woman:"unicode/1f469.png?v8",woman_artist:"unicode/1f469-1f3a8.png?v8",woman_astronaut:"unicode/1f469-1f680.png?v8",woman_beard:"unicode/1f9d4-2640.png?v8",woman_cartwheeling:"unicode/1f938-2640.png?v8",woman_cook:"unicode/1f469-1f373.png?v8",woman_dancing:"unicode/1f483.png?v8",woman_facepalming:"unicode/1f926-2640.png?v8",woman_factory_worker:"unicode/1f469-1f3ed.png?v8",woman_farmer:"unicode/1f469-1f33e.png?v8",woman_feeding_baby:"unicode/1f469-1f37c.png?v8",woman_firefighter:"unicode/1f469-1f692.png?v8",woman_health_worker:"unicode/1f469-2695.png?v8",woman_in_manual_wheelchair:"unicode/1f469-1f9bd.png?v8",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc.png?v8",woman_in_tuxedo:"unicode/1f935-2640.png?v8",woman_judge:"unicode/1f469-2696.png?v8",woman_juggling:"unicode/1f939-2640.png?v8",woman_mechanic:"unicode/1f469-1f527.png?v8",woman_office_worker:"unicode/1f469-1f4bc.png?v8",woman_pilot:"unicode/1f469-2708.png?v8",woman_playing_handball:"unicode/1f93e-2640.png?v8",woman_playing_water_polo:"unicode/1f93d-2640.png?v8",woman_scientist:"unicode/1f469-1f52c.png?v8",woman_shrugging:"unicode/1f937-2640.png?v8",woman_singer:"unicode/1f469-1f3a4.png?v8",woman_student:"unicode/1f469-1f393.png?v8",woman_teacher:"unicode/1f469-1f3eb.png?v8",woman_technologist:"unicode/1f469-1f4bb.png?v8",woman_with_headscarf:"unicode/1f9d5.png?v8",woman_with_probing_cane:"unicode/1f469-1f9af.png?v8",woman_with_turban:"unicode/1f473-2640.png?v8",woman_with_veil:"unicode/1f470-2640.png?v8",womans_clothes:"unicode/1f45a.png?v8",womans_hat:"unicode/1f452.png?v8",women_wrestling:"unicode/1f93c-2640.png?v8",womens:"unicode/1f6ba.png?v8",wood:"unicode/1fab5.png?v8",woozy_face:"unicode/1f974.png?v8",world_map:"unicode/1f5fa.png?v8",worm:"unicode/1fab1.png?v8",worried:"unicode/1f61f.png?v8",wrench:"unicode/1f527.png?v8",wrestling:"unicode/1f93c.png?v8",writing_hand:"unicode/270d.png?v8",x:"unicode/274c.png?v8",yarn:"unicode/1f9f6.png?v8",yawning_face:"unicode/1f971.png?v8",yellow_circle:"unicode/1f7e1.png?v8",yellow_heart:"unicode/1f49b.png?v8",yellow_square:"unicode/1f7e8.png?v8",yemen:"unicode/1f1fe-1f1ea.png?v8",yen:"unicode/1f4b4.png?v8",yin_yang:"unicode/262f.png?v8",yo_yo:"unicode/1fa80.png?v8",yum:"unicode/1f60b.png?v8",zambia:"unicode/1f1ff-1f1f2.png?v8",zany_face:"unicode/1f92a.png?v8",zap:"unicode/26a1.png?v8",zebra:"unicode/1f993.png?v8",zero:"unicode/0030-20e3.png?v8",zimbabwe:"unicode/1f1ff-1f1fc.png?v8",zipper_mouth_face:"unicode/1f910.png?v8",zombie:"unicode/1f9df.png?v8",zombie_man:"unicode/1f9df-2642.png?v8",zombie_woman:"unicode/1f9df-2640.png?v8",zzz:"unicode/1f4a4.png?v8"}};function sn(e,t){return e.replace(/<(code|pre|script|template)[^>]*?>[\s\S]+?<\/(code|pre|script|template)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(//g,function(e){return e.replace(/:/g,"__colon__")}).replace(/([a-z]{2,}:)?\/\/[^\s'">)]+/gi,function(e){return e.replace(/:/g,"__colon__")}).replace(/:([a-z0-9_\-+]+?):/g,function(e,n){return i=e,o=n,e=t,n=dn.data[o],i,i=n?e&&/unicode/.test(n)?''+n.replace("unicode/","").replace(/\.png.*/,"").split("-").map(function(e){return""+e+";"}).join("").concat("︎")+"":'':i;var i,o}).replace(/__colon__/g,":")}function gn(e){var o={};return{str:e=(e=void 0===e?"":e)&&e.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,i){return-1===n.indexOf(":")?(o[n]=i&&i.replace(/"/g,"")||!0,""):e}).trim(),config:o}}var ln="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var vn,hn=(function(e){var c,f,p,d,n,s=function(c){var i=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,e={},S={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof z?new z(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/=a.reach);m+=_.value.length,_=_.next){var b=_.value;if(i.length>n.length)return;if(!(b instanceof z)){var D,k=1;if(l){if(!(D=$(h,m,n,g))||D.index>=n.length)break;var w=D.index,y=D.index+D[0].length,F=m;for(F+=_.value.length;F<=w;)_=_.next,F+=_.value.length;if(F-=_.value.length,m=F,_.value instanceof z)continue;for(var x=_;x!==i.tail&&(Fa.reach&&(a.reach=B);b=_.prev;E&&(b=T(i,b,E),m+=E.length),R(i,b,k);A=new z(r,s?S.tokenize(A,s):A,v,A);_=T(i,b,A),C&&T(i,_,C),1a.reach&&(a.reach=B.reach))}}}}}(e,t,n,t.head,0),function(e){var n=[],i=e.head.next;for(;i!==e.tail;)n.push(i.value),i=i.next;return n}(t)},hooks:{all:{},add:function(e,n){var i=S.hooks.all;i[e]=i[e]||[],i[e].push(n)},run:function(e,n){var i=S.hooks.all[e];if(i&&i.length)for(var o,t=0;o=i[t++];)o(n)}},Token:z};function z(e,n,i,o){this.type=e,this.content=n,this.alias=i,this.length=0|(o||"").length}function $(e,n,i,o){e.lastIndex=n;i=e.exec(i);return i&&o&&i[1]&&(o=i[1].length,i.index+=o,i[0]=i[0].slice(o)),i}function u(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function T(e,n,i){var o=n.next,i={value:i,prev:n,next:o};return n.next=i,o.prev=i,e.length++,i}function R(e,n,i){for(var o=n.next,t=0;t"+t.content+""+t.tag+">"},!c.document)return c.addEventListener&&(S.disableWorkerMessageHandler||c.addEventListener("message",function(e){var n=JSON.parse(e.data),i=n.language,e=n.code,n=n.immediateClose;c.postMessage(S.highlight(e,S.languages[i],i)),n&&c.close()},!1)),S;var o=S.util.currentScript();function t(){S.manual||S.highlightAll()}return o&&(S.filename=o.src,o.hasAttribute("data-manual")&&(S.manual=!0)),S.manual||("loading"===(e=document.readyState)||"interactive"===e&&o&&o.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)),S}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=s),void 0!==ln&&(ln.Prism=s),s.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/?[\da-f]{1,8};/i]},s.languages.markup.tag.inside["attr-value"].inside.entity=s.languages.markup.entity,s.languages.markup.doctype.inside["internal-subset"].inside=s.languages.markup,s.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(s.languages.markup.tag,"addInlined",{value:function(e,n){var i={};i["language-"+n]={pattern:/(^$)/i,lookbehind:!0,inside:s.languages[n]},i.cdata=/^$/i;i={"included-cdata":{pattern://i,inside:i}};i["language-"+n]={pattern:/[\s\S]+/,inside:s.languages[n]};n={};n[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:i},s.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(s.languages.markup.tag,"addAttribute",{value:function(e,n){s.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[n,"language-"+n],inside:s.languages[n]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),s.languages.html=s.languages.markup,s.languages.mathml=s.languages.markup,s.languages.svg=s.languages.markup,s.languages.xml=s.languages.extend("markup",{}),s.languages.ssml=s.languages.xml,s.languages.atom=s.languages.xml,s.languages.rss=s.languages.xml,function(e){var n=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+n.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+n.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+n.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+n.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:n,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;e=e.languages.markup;e&&(e.tag.addInlined("style","css"),e.tag.addAttribute("style","css"))}(s),s.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},s.languages.javascript=s.languages.extend("clike",{"class-name":[s.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),s.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,s.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:s.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:s.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:s.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:s.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:s.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),s.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:s.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),s.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),s.languages.markup&&(s.languages.markup.tag.addInlined("script","javascript"),s.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),s.languages.js=s.languages.javascript,void 0!==s&&"undefined"!=typeof document&&(Element.prototype.matches||(Element.prototype.matches=Element.prototype.msMatchesSelector||Element.prototype.webkitMatchesSelector),c={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},d="pre[data-src]:not(["+(f="data-src-status")+'="loaded"]):not(['+f+'="'+(p="loading")+'"])',s.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),s.hooks.add("before-sanity-check",function(e){var t,n,i,o,u,a,r=e.element;r.matches(d)&&(e.code="",r.setAttribute(f,p),(t=r.appendChild(document.createElement("CODE"))).textContent="Loading…",i=r.getAttribute("data-src"),"none"===(e=e.language)&&(n=(/\.(\w+)$/.exec(i)||[,"none"])[1],e=c[n]||n),s.util.setLanguage(t,e),s.util.setLanguage(r,e),(n=s.plugins.autoloader)&&n.loadLanguages(e),i=i,o=function(e){r.setAttribute(f,"loaded");var n,i,o=function(e){if(i=/^\s*(\d+)\s*(?:(,)\s*(?:(\d+)\s*)?)?$/.exec(e||"")){var n=Number(i[1]),e=i[2],i=i[3];return e?i?[n,Number(i)]:[n,void 0]:[n,n]}}(r.getAttribute("data-range"));o&&(n=e.split(/\r\n?|\n/g),i=o[0],o=null==o[1]?n.length:o[1],i<0&&(i+=n.length),i=Math.max(0,Math.min(i-1,n.length)),o<0&&(o+=n.length),o=Math.max(0,Math.min(o,n.length)),e=n.slice(i,o).join("\n"),r.hasAttribute("data-start")||r.setAttribute("data-start",String(i+1))),t.textContent=e,s.highlightElement(t)},u=function(e){r.setAttribute(f,"failed"),t.textContent=e},(a=new XMLHttpRequest).open("GET",i,!0),a.onreadystatechange=function(){4==a.readyState&&(a.status<400&&a.responseText?o(a.responseText):400<=a.status?u("✖ Error "+a.status+" while fetching file: "+a.statusText):u("✖ Error: File does not exist or is empty"))},a.send(null))}),n=!(s.plugins.fileHighlight={highlight:function(e){for(var n,i=(e||document).querySelectorAll(d),o=0;n=i[o++];)s.highlightElement(n)}}),s.fileHighlight=function(){n||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),n=!0),s.plugins.fileHighlight.highlight.apply(this,arguments)})}(qe={exports:{}}),qe.exports);function _n(e,n){return"___"+e.toUpperCase()+n+"___"}vn=Prism,Object.defineProperties(vn.languages["markup-templating"]={},{buildPlaceholders:{value:function(o,t,e,u){var a;o.language===t&&(a=o.tokenStack=[],o.code=o.code.replace(e,function(e){if("function"==typeof u&&!u(e))return e;for(var n,i=a.length;-1!==o.code.indexOf(n=_n(t,i));)++i;return a[i]=e,n}),o.grammar=vn.languages.markup)}},tokenizePlaceholders:{value:function(f,p){var d,s;f.language===p&&f.tokenStack&&(f.grammar=vn.languages[p],d=0,s=Object.keys(f.tokenStack),function e(n){for(var i=0;i=s.length);i++){var o,t,u,a,r,c=n[i];"string"==typeof c||c.content&&"string"==typeof c.content?(t=s[d],u=f.tokenStack[t],o="string"==typeof c?c:c.content,r=_n(p,t),-1<(a=o.indexOf(r))&&(++d,t=o.substring(0,a),u=new vn.Token(p,vn.tokenize(u,f.grammar),"language-"+p,u),a=o.substring(a+r.length),r=[],t&&r.push.apply(r,e([t])),r.push(u),a&&r.push.apply(r,e([a])),"string"==typeof c?n.splice.apply(n,[i,1].concat(r)):c.content=r)):c.content&&e(c.content)}return n}(f.tokens))}}});function mn(t,e){var u=this;this.config=t,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=t.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?t.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var n=this._initRenderer();this.heading=n.heading;var a=o(e=t.markdown||{})?e(on,n):(on.setOptions(m(e,{renderer:m(n,e.renderer)})),on);this._marked=a,this.compile=function(i){var o=!0,e=r(function(e){o=!1;var n="";return i&&(n=f(i)?a(i):a.parser(i),n=t.noEmoji?n:sn(n,t.nativeEmoji),pn.clear(),n)})(i),n=u.router.parse().file;return o?u.toc=u.cacheTOC[n]:u.cacheTOC[n]=[].concat(u.toc),e}}var bn={},Dn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,n){return{html:'"}},video:function(e,n){return{html:'"}},audio:function(e,n){return{html:'"}},code:function(e,n){var i=e.match(/\.(\w+)$/);return{url:e,lang:i="md"===(i=n||i&&i[1])?"markdown":i}}};mn.prototype.compileEmbed=function(e,n){var i,o,t=gn(n),u=t.str,t=t.config;if(n=u,t.include)return S(e)||(e=L(this.contentBase,$(this.router.getCurrentPath()),e)),t.type&&(o=Dn[t.type])?(i=o.call(this,e,n)).type=t.type:(o="code",/\.(md|markdown)/.test(e)?o="markdown":/\.mmd/.test(e)?o="mermaid":/\.html?/.test(e)?o="iframe":/\.(mp4|ogg)/.test(e)?o="video":/\.mp3/.test(e)&&(o="audio"),(i=Dn[o].call(this,e,n)).type=o),i.fragment=t.fragment,i},mn.prototype._matchNotCompileLink=function(e){for(var n=this.config.noCompileLinks||[],i=0;i/g.test(i=void 0===(i=o)?"":i)&&(i=i.replace("\x3c!-- {docsify-ignore} --\x3e",""),a=!0),/{docsify-ignore}/g.test(i)&&(i=i.replace("{docsify-ignore}",""),a=!0),//g.test(i)&&(i=i.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),u=!0),/{docsify-ignore-all}/g.test(i)&&(i=i.replace("{docsify-ignore-all}",""),u=!0),{content:i,ignoreAllSubs:u,ignoreSubHeading:a}),i=u.content,a=u.ignoreAllSubs,u=u.ignoreSubHeading,o=i.trim();e.title=(i=void 0===(i=o)?"":i).replace(/(<\/?a.*?>)/gi,""),e.ignoreAllSubs=a,e.ignoreSubHeading=u;u=pn(t.id||o),t=s.toURL(s.getCurrentPath(),{id:u});return e.slug=t,g.toc.push(e),"'+o+""},t.code={renderer:e}.renderer.code=function(e,n){var i=hn.languages[n=void 0===n?"markup":n]||hn.languages.markup;return''+hn.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),i,n)+"
"},t.link=(i=(n={renderer:e,router:s,linkTarget:n,linkRel:i,compilerClass:g}).renderer,r=n.router,c=n.linkTarget,n.linkRel,f=n.compilerClass,i.link=function(e,n,i){var o=[],t=gn(n=void 0===n?"":n),u=t.str,t=t.config;return c=t.target||c,a="_blank"===c?f.config.externalLinkRel||"noopener":"",n=u,S(e)||f._matchNotCompileLink(e)||t.ignore?(S(e)||"./"!==e.slice(0,2)||(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),o.push(0===e.indexOf("mailto:")?"":'target="'+c+'"'),o.push(0!==e.indexOf("mailto:")&&""!==a?' rel="'+a+'"':"")):(e===f.config.homepage&&(e="README"),e=r.toURL(e,null,r.getCurrentPath())),t.disabled&&(o.push("disabled"),e="javascript:void(0)"),t.class&&o.push('class="'+t.class+'"'),t.id&&o.push('id="'+t.id+'"'),n&&o.push('title="'+n+'"'),'"+i+""}),t.paragraph={renderer:e}.renderer.paragraph=function(e){e=/^!>/.test(e)?un("tip",e):/^\?>/.test(e)?un("warn",e):""+e+"
";return e},t.image=(o=(i={renderer:e,contentBase:o,router:s}).renderer,p=i.contentBase,d=i.router,o.image=function(e,n,i){var o=e,t=[],u=gn(n),a=u.str,u=u.config;return n=a,u["no-zoom"]&&t.push("data-no-zoom"),n&&t.push('title="'+n+'"'),u.size&&(n=(a=u.size.split("x"))[0],(a=a[1])?t.push('width="'+n+'" height="'+a+'"'):t.push('width="'+n+'"')),u.class&&t.push('class="'+u.class+'"'),u.id&&t.push('id="'+u.id+'"'),S(e)||(o=L(p,$(d.getCurrentPath()),e)),0":'"}),t.list={renderer:e}.renderer.list=function(e,n,i){n=n?"ol":"ul";return"<"+n+" "+[//.test(e.split('class="task-list"')[0])?'class="task-list"':"",i&&1"+e+""+n+">"},t.listitem={renderer:e}.renderer.listitem=function(e){return/^(]*>)/.test(e)?'":""+e+""},e.origin=t,e},mn.prototype.sidebar=function(e,n){var i=this.toc,o=this.router.getCurrentPath(),t="";if(e)t=this.compile(e);else{for(var u=0;u{inner}");this.cacheTree[o]=n}return t},mn.prototype.subSidebar=function(e){if(e){var n=this.router.getCurrentPath(),i=this.cacheTree,o=this.toc;o[0]&&o[0].ignoreAllSubs&&o.splice(0),o[0]&&1===o[0].level&&o.shift();for(var t=0;t\n'+e+"\n"}]).links={}:(n=[{type:"html",text:e}]).links={}),u({token:t,embedToken:n}),++c>=r&&u({})}}(n);n.embed.url?W(n.embed.url).then(o):o(n.embed.html)}}({compile:i,embedTokens:r,fetch:n},function(e){var n,i=e.embedToken,e=e.token;e?(n=e.index,p.forEach(function(e){n>e.start&&(n+=e.length)}),m(f,i.links),a=a.slice(0,n).concat(i,a.slice(n+1)),p.push({start:n,length:i.length-1})):(Fn[t]=a.concat(),a.links=Fn[t].links=f,o(a))})}function An(e,n,i){var o,t,u,a;return n="function"==typeof i?i(n):"string"==typeof i?(u=[],a=0,(o=i).replace(V,function(n,e,i){u.push(o.substring(a,i-1)),a=i+=n.length+1,u.push(t&&t[n]||function(e){return("00"+("string"==typeof Q[n]?e[Q[n]]():Q[n](e))).slice(-n.length)})}),a!==o.length&&u.push(o.substring(a)),function(e){for(var n="",i=0,o=e||new Date;i404 - Not found","Vue"in window)for(var u=0,a=D(".markdown-section > *").filter(n);uscript").filter(function(e){return!/template/.test(e.type)})[0])||(e=e.innerText.trim())&&new Function(e)()),"Vue"in window){var c,f,p=[],d=Object.keys(i.vueComponents||{});2===t&&d.length&&d.forEach(function(e){window.Vue.options.components[e]||window.Vue.component(e,i.vueComponents[e])}),!wn&&i.vueGlobalOptions&&"function"==typeof i.vueGlobalOptions.data&&(wn=i.vueGlobalOptions.data()),p.push.apply(p,Object.keys(i.vueMounts||{}).map(function(e){return[b(o,e),i.vueMounts[e]]}).filter(function(e){var n=e[0];e[1];return n})),(i.vueGlobalOptions||d.length)&&(c=/{{2}[^{}]*}{2}/,f=/<[^>/]+\s([@:]|v-)[\w-:.[\]]+[=>\s]/,p.push.apply(p,D(".markdown-section > *").filter(function(i){return!p.some(function(e){var n=e[0];e[1];return n===i})}).filter(function(e){return e.tagName.toLowerCase()in(i.vueComponents||{})||e.querySelector(d.join(",")||null)||c.test(e.outerHTML)||f.test(e.outerHTML)}).map(function(e){var n=m({},i.vueGlobalOptions||{});return wn&&(n.data=function(){return wn}),[e,n]})));for(var s=0,g=p;s([^<]*?)$'))&&("color"===n[2]?o.style.background=n[1]+(n[3]||""):(e=n[1],F(o,"add","has-mask"),S(n[1])||(e=L(this.router.getBasePath(),n[1])),o.style.backgroundImage="url("+e+")",o.style.backgroundSize="cover",o.style.backgroundPosition="center center"),i=i.replace(n[0],"")),this._renderTo(".cover-main",i),K()):F(o,"remove","show")},n.prototype._updateRender=function(){var e,n,i,o;e=this,n=l(".app-name-link"),i=e.config.nameLink,o=e.route.path,n&&(f(e.config.nameLink)?n.setAttribute("href",i):"object"==typeof i&&(e=Object.keys(i).filter(function(e){return-1':"")),e.coverpage&&(f+=(o=", 100%, 85%",'')),e.logo&&(o=/^data:image/.test(e.logo),n=/(?:http[s]?:)?\/\//.test(e.logo),i=/^\./.test(e.logo),o||n||i||(e.logo=L(this.router.getBasePath(),e.logo))),f+=(i=(n=e).name||"",""+('')+''),this._renderTo(c,f,!0)):this.rendered=!0,e.mergeNavbar&&g?p=b(".sidebar"):(r.classList.add("app-nav"),e.repo||r.classList.add("no-badge")),e.loadNavbar&&w(p,r),e.themeColor&&(v.head.appendChild(k("div","").firstElementChild),u=e.themeColor,window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)")||(e=D("style:not(.inserted),link"),[].forEach.call(e,function(e){"STYLE"===e.nodeName?X(e,u):"LINK"===e.nodeName&&(e=e.getAttribute("href"),/\.css$/.test(e)&&W(e).then(function(e){e=k("style",e);_.appendChild(e),X(e,u)}))}))),this._updateRender(),F(h,"ready")},n}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.routes=function(){return this.config.routes||{}},n.prototype.matchVirtualRoute=function(t){var u=this.routes(),a=Object.keys(u),r=function(){return null};function c(){var e=a.shift();if(!e)return r(null);var n=x(o=(i="^",0===(o=e).indexOf(i)?o:"^"+o),"$")?o:o+"$",i=t.match(n);if(!i)return c();var o=u[e];if("string"==typeof o)return r(o);if("function"!=typeof o)return c();n=o,e=Bn(),o=e[0];return(0,e[1])(function(e){return"string"==typeof e?r(e):!1===e?r(null):c()}),n.length<=2?o(n(t,i)):n(t,i,o)}return{then:function(e){r=e,c()}}},n}(function(i){function e(){for(var e=[],n=arguments.length;n--;)e[n]=arguments[n];i.apply(this,e),this.route={}}return i&&(e.__proto__=i),((e.prototype=Object.create(i&&i.prototype)).constructor=e).prototype.updateRender=function(){this.router.normalize(),this.route=this.router.parse(),h.setAttribute("data-page",this.route.file)},e.prototype.initRouter=function(){var n=this,e=this.config,e=new("history"===(e.routerMode||"hash")&&t?N:I)(e);this.router=e,this.updateRender(),H=this.route,e.onchange(function(e){n.updateRender(),n._updateRender(),H.path!==n.route.path?(n.$fetch(d,n.$resetEvents.bind(n,e.source)),H=n.route):n.$resetEvents(e.source)})},e}(function(e){function n(){e.apply(this,arguments)}return e&&(n.__proto__=e),((n.prototype=Object.create(e&&e.prototype)).constructor=n).prototype.initLifecycle=function(){var i=this;this._hooks={},this._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var n=i._hooks[e]=[];i._lifecycle[e]=function(e){return n.push(e)}})},n.prototype.callHook=function(e,t,u){void 0===u&&(u=d);var a=this._hooks[e],r=this.config.catchPluginErrors,c=function(n){var e=a[n];if(n>=a.length)u(t);else if("function"==typeof e){var i="Docsify plugin error";if(2===e.length)try{e(t,function(e){t=e,c(n+1)})}catch(e){if(!r)throw e;console.error(i,e),c(n+1)}else try{var o=e(t);t=void 0===o?t:o,c(n+1)}catch(e){if(!r)throw e;console.error(i,e),c(n+1)}}else c(n+1)};c(0)},n}(qe))))))));function $n(e,n,i){return Sn&&Sn.abort&&Sn.abort(),Sn=W(e,!0,i)}window.Docsify={util:Xe,dom:n,get:W,slugify:pn,version:"4.13.0"},window.DocsifyCompiler=mn,window.marked=on,window.Prism=hn;function Tn(){e(function(e){return new zn})}window.DOCSIFY_DEFER?window.runDocsify=Tn:(window.runDocsify=function(){},Tn())}();
diff --git a/docs/assets/docsify.search.min.20230513.js b/docs/assets/docsify.search.min.20230513.js
deleted file mode 100644
index 9212c24461..0000000000
--- a/docs/assets/docsify.search.min.20230513.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(){function u(e){return e.replace(//,"").replace(/{docsify-ignore}/,"").replace(//,"").replace(/{docsify-ignore-all}/,"").trim()}var f={},m={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function g(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function y(e){return e.text||"table"!==e.type||(e.rows.unshift(e.header),e.text=e.rows.map(function(e){return e.map(function(e){return e.text}).join(" | ")}).join(" |\n ")),e.text}function v(e){return e.text||"list"!==e.type||(e.text=e.raw),e.text}function b(o,e,s,c){void 0===e&&(e="");var d,e=window.marked.lexer(e),l=window.Docsify.slugify,p={},h="";return e.forEach(function(e,n){var t,a,i,r;"heading"===e.type&&e.depth<=c?(t=(a=(i=e.text,r={},{str:i=(i=void 0===i?"":i)&&i.replace(/^('|")/,"").replace(/('|")$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(r[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:r})).str,i=a.config,a=u(e.text),d=i.id?s.toURL(o,{id:l(i.id)}):s.toURL(o,{id:l(g(a))}),t&&(h=u(t)),p[d]={slug:d,title:h,body:""}):(0===n&&(d=s.toURL(o),p[d]={slug:d,title:"/"!==o?o.slice(1):"Home Page",body:e.text||""}),d&&(p[d]?p[d].body?(e.text=y(e),e.text=v(e),p[d].body+="\n"+(e.text||"")):(e.text=y(e),e.text=v(e),p[d].body=e.text||""):p[d]={slug:d,title:"",body:""}))}),l.clear(),p}function p(e){return e&&e.normalize?e.normalize("NFD").replace(/[\u0300-\u036f]/g,""):e}function o(e){var n=[],t=[];Object.keys(f).forEach(function(n){t=t.concat(Object.keys(f[n]).map(function(e){return f[n][e]}))});var a=(e=e.trim()).split(/[\s\-,\\/]+/);1!==a.length&&(a=[].concat(e,a));for(var i=0;il.length&&(t=l.length),a=c&&"..."+c.substring(n,t).replace(a,function(e){return''+e+""})+"...",o+=a)}),0\n\n'+e.title+"
\n"+e.content+"
\n\n"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=r||''+c+"
",s.hideOtherSidebarContent&&(i&&i.classList.add("hide"),n&&n.classList.add("hide"))}function l(e){s=e}function h(e,n){var t,a,i=n.router.parse().query.s;l(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0.6em 7px;\n font-size: inherit;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.search input::-ms-clear {\n display: none;\n height: 0;\n width: 0;\n}\n\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n=Docsify.dom.create("div",'\n \n '),e=Docsify.dom.find("aside");Docsify.dom.toggleClass(n,"search"),Docsify.dom.before(e,n)}(i),n=Docsify.dom.find("div.search"),a=Docsify.dom.find(n,"input"),e=Docsify.dom.find(n,".input-wrap"),Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(a,"input",function(n){clearTimeout(t),t=setTimeout(function(e){return d(n.target.value.trim())},100)}),Docsify.dom.on(e,"click",function(e){"INPUT"!==e.target.tagName&&(a.value="",d())}),i&&setTimeout(function(e){return d(i)},500)}function x(e,n){var t,a,i,r,o;l(e),t=e.placeholder,a=n.route.path,(r=Docsify.dom.getNode('.search input[type="search"]'))&&("string"==typeof t?r.placeholder=t:(i=Object.keys(t).filter(function(e){return-127MyWX-Je;l!S~*SJ9!{gRVN<
zQkT>%gCT^*E5wvX=@8Sckcv_1I7jWX#=YzFUElTneSeR={@E+vpX0zpdO{W*c2-bozX@iFb}(XGcvaoHodtPy<1FmQB^KEq{?YG({<
zXO3teQvjCZ!saHgAfAzHW-NA?f4)GVOJXx
z^bpS(AnbaG4{9a^w&FRpgn-sHv=DeL$j%mme>0-M1BCkk&uJ!bn-ISygwuqu8wtJ*
zMBfI)w-NDaBmj8UA>JSqgAnl&E3}UoVT%dgwSX0H6A^*Q7VEMa2y8JCfF(v)4GIQX
zuX>_)J<+3%=v9v}L`2VeqPxhzzyLTVrluqkS<&uZXK7_cCX)?JY>2KR#8pHDrfV(Y
zT1#}RMHmfq8%KQu0}6$r>v)F9tR*r;RBJaYD{IiQefxHj!x?KETLS|_J$-#LJ%-Hq
zk7-af&C!C=_h+LA~lkZMg{tO5T6n#$t_Kn^mWi%+zYm}tPAIU!o#U9~bZ-c*I6
z=p0%tL{Z`B@>z6t;`XAD+JtsEXQ)33RYggYJ}3(I|5%|=EKwh|TPNc)(LgOBDnl#N
z%W-IG^9do!W7dzQpn-FzgeY8K3MNuAGe5d0W`IGcDmn?iH3>$MfbiM9I~+aW#GA{f
zGH=gq6{3M?hI4E^`l@Biz?6MOS>RYkS7d*%WawuWOU{zXI~1#lvRf8W!1gnQpmk5#9wZpH(=VV!D$?Qo(ZT@S|HxnCgN;z*)%XVYKhe1$g%1bMV
zy7ww{UL)#Fmy-(f^u`!!c*(_|i}-kG0+W+>dgg@htR{CwcUCg57%Lm6abuhF>m!B!v8g+A#)Bo*8}t)ds_%vhWHDJ$x`MK;=E}xPUKJM#F7sl?
zG$8Jnu`P3i$My8-cW0WL?$VH4h+r>SY3Ei9o=xh-s*m+2?Z>bJvI$*+uP?$jK}P3)
zwv=CT-xsal*np|s^BZd^@t?&?{gBy4Y>s}-lA&Q??>k)lsiK4N2|0;Pn<0dzx~N(C
z!W=WAHukq7^eBgIQ}m1nRdD#~(^3^Dv~~frUSp?b^rwK+XZF9`G7x;;k%Bdl$7ZQx
zv6aPRrhi_!ooAa`_^(}5)BaW3a_y^i#XmN8yjHSV_bXpL8#`S%9EFr?
zH8~+?$A6-SUA-X|T(vzhl45oZ$vb`kzLVCuMU~)9Oy0Ww5k2t*&3(t7bIJv-yydC9
zu#-=`>|+?KBltGae0a5MZQ<;Kb)SvtcCG1KGjEk*SYLo
z9=s(7uW|8P@AZuzhw>T{;^uWZDzAhbLYmd1lk2YU*+dC`pAzd$tXe(r
zV)%U_u`HoaMjbaz>SO4NGpv02objGPzZN+EiRiZEzxm~D$NrK~c>4_{lUMSR5BHR-
z$e2$aFOv(xa{3K>O+HuB$hWHg%i`Q68az*He5;X)C=>elw@pJ_@SDDrbGvT@=;ziP
zLf%#X+vvbLh%uCGd&zy;I%QRG-G3$K9+
zKT&isEcmC1<_+i4pg`;V+Ifs;g+>zTcURfCiu96EPy5{(HEAt}6Gx65jfXM|54jlL
z>MX1|(neO5@6;=1x2&&OcFE(1J@e6vD^{LfpD&uYb
zk!rQYAHIn$=%fDN+-h~h?3~r9W4Edyr)TJb!2jq%pjNqZlCbw|Ta7;S<3so)vQa+q
zgJ!m)?5^qwkI$_%Gpw{K&8ET#IxqdRHkZW8#O@Me;pOaVJ~rl?riWX>&9-m14sY-;
z9^-te>7#6MX>HBY<~+ixY}@W?4KLR_GRuA!;pd4e(t#M`4~eMN2hW3)&S_;=I+jw`
zBR%raerY&2_#uTNpe4Hw8=m-#iH-fi86IS742%S7Q$*D_P330fYK
zI1R%9si=bG!
zmvP&g0>>WP>OdGp%yZjxpxQW01DrN{0CA3t%N8dX2J*8&7+FlwZd#p+uE$)m6P&iT
zj*PaB&VL;p-3qb{P*GKt(bqS44K!e;xrL<`$p*;F%ZsR~sT)u&4*>v_Aazfxk+dWL
O0000D
diff --git a/docs/assets/favicon-196x196.png b/docs/assets/favicon-196x196.png
deleted file mode 100644
index 4e7f4e0e7307f974292b11d5faa9e43925d50b66..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 5631
zcmXw71yoaw`yM?yMr@3ZkwY4!2Lq?1NRL)zh*MfXattX25l2al4`riVh;l$65bl#WtRwi_fAobifxH`?
z;0}Q>ZaLUES)K^70UhClDIpN?)5=FYB^o*=lrSR{J1HDLC7d`doG=Xv$;2hF;K+$P
zf=JB7QOngyp|f2_I(7mPHX;;1DGWNX|N2)agkvWmiRr>IN8*1RI{^xzE91h^<3d;H
z!XP3+9v6xnKhjZjk;pL-FbkTIbhualk%$J(BVRaVL^ym@Bz#ObY*Zv-OgLgx1avMA
ziGX3~i16iMFcblqI3#l66$p6nD>&f=Jm@7n@P)|v=OEx9&J9YQ?tpvuiv$e9&kn!?
zp2GuPAp8d*=3NM{ez^aDh+jYGOFDNU&JH5IdPRK(;6D9Gf3SEE;nNTI?ML|bA^Zo9
zLjQh*cc18y*$3AA#|WQZgm*vOrw>da{$)76N5pFo;n|D8_aHobj@Y9|6ogwh!o3S*
zQI{?-M7Vc@j64lGT?p4Mq+|P$55lQa)Ug9`rbF}$DB49&w;>(ckao`yc2AM^&ycoH
zL5;L|inIpt7HTNhGF8%)A*i
z#mrkkt*NCgAt8Y@1I;FcX_FY3wP*%e&FGw_rq(fabxA2HNi`49kua@6p%m0LG>@rk
zNJ~qLnKkL?>dMHgl7^)U>pPKW?C|sR`C;XlUvmKMtnA4sdN58Ch|0
ziQoBJdEiBV5aDQd2EtgkG{IWHn02|Oo-~hnDe{(r63tj-!zkbCsdX9hzU>{f>
z>jSob{W=6T8iWf70C4XEFrE74P#*xM!Tj_+;1B?&?g0Rw2Ph~7HUO6u;L!JZ9MAMUeQ?a-z3G4XXyV~4(6jTY
zI#+I^u@F3bAE3@*_1<#?_>BdC5r7$hQ}%c70Dt;`h3yRh7;;c-@Q=^~w!u~Y|C13l
z29)jtcfj<2_MLU$J5aJ)Qk|BT###rx|A3Nm772mycb>#zoT3>PXHRFVDDYhyU;mak
z8Ix60RauiFB>yZiLxUJD;d_zqkRM|%U$xmBw~u+nR-9srHf5lDGZaW^eGDi*+%A<3
z_M5G^U8P)SKN;O6sQH}twsQ3*gR~IL$ug9E^JLhmzt5Gb?J3e(d1?LS`?g~%oI
zJ>0fpwKd@8sbXh+#$4TA7*dyhOL6++D^IE-I)zO@4Fic?L{GfQUNH{gRCadG>AJ4`
zYy2`~WDR0|36u8n?(c(^;Tx4T&e`$$YM8qE%^=Fu(~h}Un=oqZ;;3iJXMj`?+?o_c
zVI^!L9rg{aE}IbN#z+bdJD*hE1;%dQDMVE0rQ9*=Hdd5>skt3b&3n~EB|e!@fZ^gf
zKgnd*?HqKx$=Ma@VxKR+gb8w|*V9=h*|Zra?$hH*0XU;L0ECu{s;Ik$+;
z!RSev5IDJhW<8mG)A;90lkamUn#U2{%mFGPoUcM-g9aZ24qy$V44f93Mu^dN@g`$B
zw@yX*CJ7kauC;b>%Kp4vsE!V?*fr(?0q^W=p
zpv5FOW1?g
zUDO($O*fAk`|gD+n#)snQ6RA+`-YIY)eO&dy&4+*iVE(v$vj%uxj-%ypuQ>sEhP93y
z_sYn~FV!`E%vYgnmFmJ
zfmt)9)&9bBt^#kntvV6SN?};pIcpSrf!UijM8D`~ysmLdksHuWy7ju2#Wm0rq277X{vTbOguUOdq>orO5A7k&w5WN2$ji
zkUYLb^Z8{56+LEnH`n=@4F~*;?-HRt@ILH3m&g}c0fg^;=XaJOV0Y3!$H?BlH(0jr
z9yc(;v|=gDVv||q@QA4@VehyGopNQbw7{TuKES4I;7;(?HKh`|^v(1jh)+i0KIB_n
ztaL!JAb$!rof+~2cjherM5FOmp`IA3c}_KHS3}F!;Ex__e`Yy3S<0UrO0Z!b(yv&q
zk>bhE`yD6Y>+3xnF0&!G>3Bl1ztD)2MbFY{&K>(#PKMuLC^3Uhlz9vlE4Yqb^_PDY
zTxeKyrBh*OMY9|2rW(HdTh1k>PF9d$TQ(pvF!2Z~wWGK2UDs@$)c(f%eMJS=>yVvS
zs1LXzw*9VaDwY)Xt?bF^NUK%j*^e8#J%P)2pBVJtN}pWF8C-5Le$16Ksz1_k(SG6B
zNPDB*0@3(no}KYq#b4sCUYG3^tmPDidQEmwTz#@;JQ+CFMKZdFv*uc4U-tzr=_N-e
zYpHglmR5EI4m)UpFSz+n**1L!ZP}_RHm`YYV>WM1je@kvrzgns;jH{Ijk^
zw?yxsG5q(R&+a}x6<4YghEi=_+Z8>0)VkkX=2rB&aV;4h`*QnMNVs~rwVr0_Cv(!h
zg4~FQT~LxOM4~SD#ou|k#gdLGLTuAlbaf{`^BuFmsCuYc0?fqbwF#i-I27R4Tj!(pJks>Y-rvlYs
zc5sR=j*4eIPs%HdPfgvBWm)1R+0u1X@+pmyE{i3n*BrGu{3)vRgNM2cygf3jGMu}-
zQ78MvHT`aTFmLum`_FvOeaD7*2&DyCW79dV)CO^4zxmCJt}}u%_!(!f$=wE9Y(!cy
zttma;2A`D<&r6rJ#h)3LQ6=G|Q`E1^p0$a7G8_5h!9ShR1cu(}ge*+2AWIJ_i7nZn
z#`PTk)HyFt&xT_RlXTTyxp%8h{rbn!o6jw)+tiK6@aXi^|H}K^udkq5)NShB4y*gE
z#@C%=>)ng#)#fB&YVWU}=Lc5ws7s&adr<^lVI`yNF*2KEshzoBVEtLQ#xL#iHe){C
zqfB+7@aw(jhuCQ)WTp^E?D`<5jJAR{%lK8}?Z3BvA~#Op{I&BQbGqev|BaJX8Ay0E
z?J$)TAaUx>bx386im+Jdvy?9^IevnC{1zSrPQG|HF8UFnwAihJ3^Zs@f^tQ{dZDDJ!`pfGo1gpi8v1H0pHn_@RtgALx?#?r1Pg)+?>$qyGtJ)Ed9D
z;&!3c1D|z+hHuy{QgmE0bWd-=>asRCPK!I0-gQA}hnPim;w-*Z2HIN)4hy*RBO|>o
zH!;Z?M;7{6wD_$O(jWE=Gh5?Wq(jm_PI|H!JTx3Q_Lt7dkdh2VN-aY9CIpO`+04`9
z4Pg&|O1cKKRaq2g$aZMFH|dEUYxrQit>H}k;1^lze2wdG62~AOx`lvpWqI3H2rws=
zV;(Rp{Fw7(N@h7HMPdbJ>nr4?SJYy6s^p*xdqx|nZS~}PmO)hp8Y+Ia=T?Prvj4u9kCY#Ld=sp!V`NDTXymW5LgRJ0^nV%3zDHGFa=@vohY&9EMNI95GH=Uws(c4FkQ
zB51EHTxup0UTAmhDY(T`0%W}(quimtxfX+^ynWbZE~zW#)*L=|l_e>vdp)9H!gf6OdKoCZ
z*FL*~Q>A|zBriaE2A8RWYT5mrWy?!IVGx85h&c`uOf#2C9~J7DUfTWsM7H4!`1s&V
zrx=&YI29jk*9E*=uzRM}2e)`=5mTgF>Gw&)ECQ{8OAp#ue6QR3)16iD>WOa
zA9AWU$M$3q8Tb2mr{WoF!U!Gl3)SsQc3=&E%kzLJB5vm`vK;QP@kOmVoOf;Wnj?Wz
zy4V~KJh-+1^;4r@*L+U}ojK%a@v;F%SiH+WFd$kP&OTG^+QN*h(Ey~jj+bD3*g
zaQ+tG19B|`@y&zPa9e7_FYdK9GdvXZ&C`7xN0moTE2j7UgD(se)YI;fC|
zmAZs$YimWw=XZ!U7kV?2D&&_C&{$U<0NXR1^#??WtautCA-od
zHvh`CUI-I;FhciM*Qn+gP{%DHx%b|o`0$A$y8H(J4drMsI2rfFB*
z63y5zgfdzmc+DJ?f3q$Ag}iJ`&RXMFgD3Yto4jko?XSnjm^peFVVqj}G?=v{3X?p_
zFHXF?{K&+kWN+=`pUxEI`2F(S*K4<(mEUIgUV3}=IO&xA>X+)n5B$N}(ha6#zq8?tAabb7I^}C_7?{a?sm18
zB=ipwWgk&xY%UV5yZejps?RZeQ9G68Q0H04V*i%Y`43wf^2Bq6p`nw{*<4s&K_lQCaUi@#Mgl
z-@OBa<0~5(Wy;YqEv*rjZfDbIQB@zYe~(UizEerr1D_=^ueV}f!OP*kZSMsB65(r9
z-exv({#jn;pI<4!R4L{`u>)V~Bmg9$n#Y|Bwx2rOP_11)V~N=a!h1&PFTY#X^^+=^
z`d~pick9ZN@JluaVe3*7Sy|;%O!FLkj^8U9%rI98*2kiCMOD-M{V5Jt2rBf<@6?9#
z0zC@fsc7`OqOHkinj1wo`R6rP?;Mt4+WBFUA3_BiQ0$SZG{(GU{(19c{`raKLO8cD
zo@+pjsz!HlvjT6$=6L1oK>uYX6zUK88{Nfs#yo?2o^+j_FC5~zJsAdtwP{oB=q{_B
z|8sv*HA6jR1>53_PqPtJn7QFpbUPB)`NroLNjQ)oF!hrYpY`>&A-R&XG^v5H+CYXWQO`8?M8;Xm3RgEP<#+J-39+
z_~qeMbG#xS49Ze@Sa^=t-#%VD%Z%r*u-@lU};40E&j^FQ!HOK2m7nuA}N@O3{
zGW0C0KvNW2STZtVZ=pMBwu=4z%jUWS>!pqg@ZUW-Y5y4|h7hc1rBiy<~$qJ^8G{nt*=lAn>PF;5C
z+}KGsvQqpJ08nUKum==cOVaaj_JNLXT@x;6-(Idpi?g#29A1tLyh&2$!j2Cg9=pvM
z@upQlDM(MPPr)?idU}5n$-rJIqrWJoFn_P6f%}B<=$^ceR-@g~;^An@oR&C!
zY=g%|!Z#?GA~?cpRUN4Zu_iOzrnP7A-K&7|zTkY@Ew9`LG(zx>G=d@0kW=J^wV}!Whk@Q9oL$1Mgu2-)KQjTH0Y7&3#G#2NhRi
A`2YX_
diff --git a/docs/assets/favicon-32x32.png b/docs/assets/favicon-32x32.png
deleted file mode 100644
index 9880206ec9970cd5e90f3f8d5a3b31331daecf82..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 695
zcmV;o0!aOdP)_>@**30Mo3UN@`qkduB!B!a`7(>yxj7VG
zg;&Ui+ht(oH>L`FjeqgA7Wl^g;`5UiSbNhC&W5bJRUgd_S#$IJe{e$9-Wm@B8Q&E6
z+;3U?p}M@l3fB)VYRj*;3|Z;s!qNYbt*vmU!brxBz~_F>BJ(g~c>!&iQ$}0r?9`Sx
z9^;7uUwxSWjphR1P_o{Ud601>rT;)%nAcfbkmu=(^2_+*zas0Ewjh77@&SELPG|6p
zxw$?mK-MczD_~5|ET+%M>It4PBTN3u|32c~5B!Chl$hy7=$)9>
z7`jUGqCaPW%ecC{z=Tvk*!gXIQgHJM5D)2K3i3{V(`}$oU`%o)b4;=WnT)SW-au_=
zD9JlXRysJ0ZKdyKj!5hbo;5PbCqE%suVAqHfgy37!Lx?O`=kI_ugoFw>IJL;F`dD)
z2FCcL09mgK(Zo{Y=mQ`y!W;^#M;b1W60nJtF*IzuhbH
zvu4}f!~Y3;PoX=sgqH%}?W$c42cX-by1YP_P(O&aJB1EMbM3BSTmDIxut>Xe=vr9J
zIPw3!(EK{BSFWzsWcEYG3I#e|D+L3;(OKZD`HQcv!1syR`5;0dhyeh2gOTsoOLDaD
zmgv|*;ZHzCSOvRw!|QFVrB>Rf_y-?C516TKJF?DQ1vg`C4~S%}-F^W=%)ALHRUcv`
z%==AiLx7pieb1L@nx?nb(cyUdD5W3)z_}k_Ue=x9P?lvcrTlIq&|_t&HcM;{MYu*Mtm8Iv2pK}8clzVKuj{$L|2^09+}G#+Ty}G@--09J0AP!w1H)aK
ztnUZAN!rJ5oY@b6t#f41J>yX`H)L@L4KVLXq1LV6w#MmAKHk$j!hgdi=y=K7Jf>pL
z$EY-K%<=#6X$l{|Nvxdkb$yeb`s}S?*zddA%3tG>PVKd&&rNd6z3>HO6~==`{Z&J3
z?7G;v-SjqD1*4CZEhN+E(l8&)w@Vslsy};?v*$)b=l`oDKf!SC!_i;^R;tKckejPf
zBKh}gO+~A9y$R>lsH|A>QdH8o$S9-bcwp*Zdt2tq1^V8DiR?;T_c$aWt`DwqI5A-lY7;#F*Qi+*2g(RoCIrq{30c
z9eBIF?ND-Cy+xdsU((D^5jaG~?oGQgumWd4rS*&IscLQ!Yh!uG6BbCiK{GnK39XTC
zGSH&kUaj3v5c|HH60&__wIg0y^;+*pENX1$vt4H_p0Rxv{p$2s(~}*EkAvn1HDA!W
zs`YAmRSm&rqapvhjC!q%niUqe?k!}|#v7)fy&8QdZUY=S+El%gK8S7X9EUoqOmvq
zZK;KupqPR0+=??O8g|6c1Xx5sMP=%s3q}>7)jl)SZzN>-fx_)w#}B_%{)IB}TA|i-
z_Ne!}V`4kP?hTVl!KqP-NK|b!Qe01nK5Z4H_!YZfy-l|0{xauS-fquRrpn$~z28pv
za0f_F9xC0lho;jU9n$j>G|k+yqpt9?GQ#WrMgylt&5uWe{GV>ixE7{Vl?O-y1;oP{
zx-cD~(NRIbK;Lb4K$nu>GI?1TfkL)uW6$Bhh(L#?XAW^ZGTfVvIh}2%#f9?HUnqYv
zGZT-$lIzn3Up
z`ntuoyeH9(LxThD&ppn`O0ls*Lj~V7qg$OsAG-K7!x!&tY&={!eX*CbaOu%M^dkkS
zPYyqCE14G6JsMm|q{E1x`^y#YPuCM;-bHpa3phmhK$Mrr?CRz~i{M_4929Kp=GR2K
zFRrRa0P6&XfY=>RKOC>s+P1Gq9H>5689Fn+YI_U^4O&>}QIIqKiNL1fLJBs)CHqV)
zLP2L5Ffu}au0sEZid~nkbm96zYm=agTUbzr8>;lTU#%pEuJoCAuQxIKfR;eXjKfGTR+$y
zk8)6@{{Y`!O*ek6R-BvRXWXp63wU<7;mv%bgK^Xd42>$^2Jf`s13Sv`+GE#)0v-|6
zH-x2VmW#Yd89pSa{Xu@jpF5d)s-aN&^E=a9VRNE$_SEy;)@A=jwOjtVvUfLgp>ub#
z%DC^!i!bKx{i6Hbxpvl+rPb}cHQig2ku1#Rg--p_`Iq`3lsA2~E~W1rK1gJy2qqQk
zZm~R_p)6NfBqELZ5`&@z+}i5mug7^Zj{9K+
zZOXTjBBz#|*;n$NvUF;&lrlWHT?JMy)*aFff+_w?JgVTJ{kEj&2+Udg_xmOk2uPJl
z2LNV#;<7dMzXmh=p2Sxl8#!zzZ
zk&*2}QIKdWQJ}pf%S&isx0Oi#z7!IVK_Mx@Bnhwc_XS>i8`=i6cyon;3tL{O
z4iQ?0ic9pGO07_G=3t`3S#M#h!c>YJ9O9W7?OHebH6MCv2$0021_8OU*g5aZr@c_%
z9qsVIkzi(4q6n9vpg0F;A18P68-iS2(NGsI5)UOTO*zYbms=Kg%z5v(k#cstOP&$X
zx-9nVjqJ=nXCp|mt=Z`**5qgBHcPO^SDjpUw3$$!bd;S-RlI5u-oIG#CEyH0s$I5L
z7;E2!B$?V2M>U@0;`L;;tX7YlT5?_JG)b}%Ht@hW@@*BNYYeslXe^YF&`
z+=1hPSQPC?*Eys-dyiF2M&;*~vW8k_lrFYtUOw5WYy5af=In^uHKj3Ib=#eEJ@zjz
zJvntlzQ!I}t%`wOIl9x)YmuLXli`*w?(#J5_@VuJp$eXOIUE|iUiHu68UAm0t<5TC
zF1!;*x4;nJI9>s@eEaQLH==n@R0g+yg%WjabB{wO=WEiWB|};B$cJL`)mQ
z5Wk>cV!sT=yC)qDVG9#hh(Xhu?H_Vjyp=zv7^K%+5JC@0Jq$|=Y@9>oZ>4agkJw|w
z)iqz0kn|(#EbWd&;Rj+aO&U7r8m8sd1UBlYjaeIpr6r+giR3O4pK@ijp+HuuoQl+t
zW|IUP$+pEfvx)79kJ+u^j6jy6qM~piyRzsfKS8$fO4;RdGSDPmUR{FuSB#gl8yg>c
zdyll9@d$8Nv0HUEZ^j+16{`Mi5i73Z_|kWH;|;mf7P?J^_A-;d@}<%PM>`irg$;}I
EKOGtGv;Y7A
diff --git a/docs/assets/github.svg b/docs/assets/github.svg
deleted file mode 100644
index b83e0eb158..0000000000
--- a/docs/assets/github.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/heart.svg b/docs/assets/heart.svg
deleted file mode 100644
index d5bb68f840..0000000000
--- a/docs/assets/heart.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/hourglass.svg b/docs/assets/hourglass.svg
deleted file mode 100644
index 1f90961417..0000000000
--- a/docs/assets/hourglass.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg
deleted file mode 100644
index d165ec1e82..0000000000
--- a/docs/assets/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/note.svg b/docs/assets/note.svg
deleted file mode 100644
index 6bdc463204..0000000000
--- a/docs/assets/note.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/pattern.svg b/docs/assets/pattern.svg
deleted file mode 100644
index b08a10b6ba..0000000000
--- a/docs/assets/pattern.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/assets/pro.svg b/docs/assets/pro.svg
deleted file mode 100644
index 9c9800cde1..0000000000
--- a/docs/assets/pro.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/docs/assets/style.css b/docs/assets/style.css
deleted file mode 100644
index fda1e83eb4..0000000000
--- a/docs/assets/style.css
+++ /dev/null
@@ -1,311 +0,0 @@
-:root {
- --white-5: #ffffff0d;
- --white-20: #fff3;
-
- --base-color: rgb(255, 255, 255);
-
- --mono-hue: var(--theme-hue);
- --mono-saturation: 10%;
-
- --mono-shade4: hsl(var(--mono-hue), var(--mono-saturation), 10%);
-
- --theme-hue : 208;
- --theme-saturation: 100%;
- --theme-lightness : 63%;
-
- --base-background-color: rgb(13, 15, 21);
- --base-background-color-trans: rgba(13, 15, 21, 0);
- --base-font-family: 'Roboto',sans-serif;
-
- --code-font-family: 'Martian Mono',monospace;
- --code-font-weight: 300;
- --code-block-border-radius: 0;
- --code-theme-background: var(--white-5);
- --code-inline-background: var(--white-20);
-
- --blockquote-background: var(--white-5);
- --blockquote-border-width: 0 0 0 2px;
-
- --heading-font-family: 'Roboto Condensed',sans-serif;
- --heading-h1-font-weight: 700;
- --heading-h2-font-weight: 700;
- --heading-h3-font-weight: 700;
- --heading-h4-font-weight: 700;
- --heading-h5-font-weight: 700;
-
- --heading-h2-border-color: var(--white-20);
- --heading-h2-border-width: 0 0 2px;
-
- --link-color: var(--theme-color);
-
- --sidebar-background: var(--white-5);
- --sidebar-border-color: var(--white-20);
- --sidebar-border-width: 0 2px 0 0;
- --sidebar-width: 20rem;
- --sidebar-nav-link-before-content-l3: "";
-
- --table-row-even-background: var(--base-background-color);
-
- --selection-color: rgba(255, 255, 255, 0.3);
-
- --search-input-background-color: var(--white-5);
- --search-input-border-color: var(--white-20);
- --search-input-border-width: 2px 0;
-}
-
-.app-name-link img {
- width: 90%
-}
-
-.content::before {
- content: "";
- position: absolute;
- top: 0;
- right: 0;
- width: 390px;
- height: 300px;
- background: url('/assets/pattern.svg');
-}
-
-@media (max-width: 768px) {
- .content::before {
- display: none;
- }
-}
-
-@media (min-width: 1200px) {
- :root {
- --base-font-size: 18px;
- --code-font-size: 14px;
- }
-}
-
-@media (min-width: 1400px) {
- :root {
- --base-font-size: 20px;
- --code-font-size: 16px;
- }
-}
-
-.markdown-section p {
- overflow-wrap: break-word;
-}
-
-.markdown-section pre[data-lang] {
- border: 2px solid var(--white-20);
-}
-.markdown-section pre[data-lang]::after {
- display: none;
-}
-
-.copy-code {
- appearance: none;
- cursor: pointer;
- position: absolute;
- top: 10px;
- right: 10px;
- width: 20px;
- height: 20px;
- border: none;
- padding: 0;
- background: none;
- background-image: url(/assets/copy.svg);
- background-position: center;
- background-size: contain;
- background-repeat: no-repeat;
- opacity: 0.8;
-}
-.copy-code:hover {
- opacity: 1;
-}
-.copy-code::before {
- content: "";
- position: absolute;
- display: block;
- font-size: 16px;
- font-weight: bold;
- line-height: 16px;
- height: 16px;
- left: 0;
- top: 50%;
- transform: translate3d(0, -50%, 0);
-}
-.copy-code-success {
- background-image: url(/assets/check.svg);
- opacity: 1;
-}
-.copy-code-success::before {
- content: "Copied!";
- color: #77b33a;
-}
-.copy-code-error {
- background-image: url(/assets/cross.svg);
- opacity: 1;
-}
-.copy-code-error::before {
- content: "Can't copy";
- color: #e65855;
-}
-.copy-code-success::before,
-.copy-code-error::before {
- padding-right: 8px;
- transform: translate3d(-100%, -50%, 0);
- transition: transform .2s;
-}
-
-.loading {
- margin: 150px auto 0;
- position: relative;
- text-align: center;
-}
-
-.loading__spinner {
- --spinner-size: 50px;
-
- box-sizing: border-box;
- width: var(--spinner-size);
- height: var(--spinner-size);
- margin: 0 auto;
- border: 1px solid rgba(255, 255, 255, 0.7);
- border-right-color: transparent;
- border-radius: 50%;
- animation: spinner 1s linear infinite;
-}
-
-@keyframes spinner {
- 0% {
- transform: rotate(0deg);
- }
-
- 100% {
- transform: rotate(360deg);
- }
-}
-
-.sidebar-nav li {
- font-weight: bold;
-}
-
-.sidebar-nav li>a:only-child {
- padding: var(--sidebar-nav-pagelink-padding, var(--sidebar-nav-link-padding));
-}
-
-.badge img,
-.sidebar-nav li > a > img {
- display: inline-block;
- position: relative;
- height: 1em;
- top: .125em;
-}
-
-.badge img:hover {
- filter: brightness(1.2);
-}
-
-h1 .badge img, h3 .badge img, h3 .badge img {
- height: .8em;
-}
-
-h1 .badge img, h3 .badge img, h3 .badge img, h4 .badge img, h5 .badge img {
- margin-left: .1em;
-}
-
-i.icon::after {
- content: "";
- display: inline-block;
- position: relative;
- top: .125em;
- width: 1em;
- height: 1em;
- background-position: center;
- background-size: contain;
- background-repeat: no-repeat;
-}
-i.icon-note::after {
- background-image: url(/assets/note.svg);
-}
-i.icon-warn::after {
- background-image: url(/assets/warning.svg);
-}
-i.icon-check::after {
- background-image: url(/assets/check.svg);
-}
-i.icon-cross::after {
- background-image: url(/assets/cross.svg);
-}
-i.icon-hourglass::after {
- background-image: url(/assets/hourglass.svg);
-}
-
-.github-edit-btn {
- display: block;
- position: fixed;
- bottom: 0;
- right: 1em;
- background: linear-gradient(0deg, #1d40b2, #1680d6);
- color: var(--base-color) !important;
- text-decoration: none !important;
- font-size: .8em;
- padding: 0.3em 1em;
- opacity: 0.75;
- z-index: 999;
-}
-
-.github-edit-btn:hover {
- opacity: 1;
-}
-
-@media (max-width: 768px) {
- .github-edit-btn {
- display: none;
- }
-}
-
-.links-menu {
- width: 100%;
- text-align: center;
-}
-
-.links-menu a {
- margin: 0 .5em;
-}
-
-.links-menu a img {
- height: 1em;
-}
-
-.sidebar-version-select {
- position: relative;
- margin: var(--sidebar-nav-margin);
- width: 100%;
-}
-
-.sidebar-version-select > select {
- appearance: none;
- cursor: pointer;
- margin: 0;
- width: 100%;
- background: var(--white-5);
- border: 2px solid var(--white-20);
- border-radius: 0;
- padding: 0.35em 0.7em;
- color: var(--base-color);
- font-family: var(--base-font-family);
-}
-
-.sidebar-version-select>select:hover {
- border: 2px solid var(--theme-color);
-}
-
-.sidebar-version-select::after {
- content: "▼";
- color: var(--base-color);
- position: absolute;
- top: 50%;
- right: 1em;
- font-size: 16px;
- line-height: 16px;
- height: 16px;
- transform: translateY(-8px);
- pointer-events: none;
-}
diff --git a/docs/assets/theme.css b/docs/assets/theme.css
deleted file mode 100644
index 3de40cb2b0..0000000000
--- a/docs/assets/theme.css
+++ /dev/null
@@ -1,2 +0,0 @@
-.github-corner{position:absolute;z-index:40;top:0;right:0;border-bottom:0;text-decoration:none}.github-corner svg{height:70px;width:70px;fill:var(--theme-color);color:var(--base-background-color)}.github-corner:hover .octo-arm{-webkit-animation:octocat-wave 560ms ease-in-out;animation:octocat-wave 560ms ease-in-out}@-webkit-keyframes octocat-wave{0%,100%{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@keyframes octocat-wave{0%,100%{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}.progress{position:fixed;z-index:60;top:0;left:0;right:0;height:3px;width:0;background-color:var(--theme-color);transition:width var(--duration-fast),opacity calc(var(--duration-fast) * 2)}body.ready-transition:after,body.ready-transition>*:not(.progress){opacity:0;transition:opacity var(--spinner-transition-duration)}body.ready-transition:after{content:'';position:absolute;z-index:1000;top:calc(50% - (var(--spinner-size) / 2));left:calc(50% - (var(--spinner-size) / 2));height:var(--spinner-size);width:var(--spinner-size);border:var(--spinner-track-width, 0) solid var(--spinner-track-color);border-left-color:var(--theme-color);border-left-color:var(--theme-color);border-radius:50%;-webkit-animation:spinner var(--duration-slow) infinite linear;animation:spinner var(--duration-slow) infinite linear}body.ready-transition.ready-spinner:after{opacity:1}body.ready-transition.ready-fix:after{opacity:0}body.ready-transition.ready-fix>*:not(.progress){opacity:1;transition-delay:var(--spinner-transition-duration)}@-webkit-keyframes spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}*,*:before,*:after{box-sizing:inherit;font-size:inherit;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:none;-webkit-touch-callout:none}:root{box-sizing:border-box;background-color:var(--base-background-color);font-size:var(--base-font-size);font-weight:var(--base-font-weight);line-height:var(--base-line-height);letter-spacing:var(--base-letter-spacing);color:var(--base-color);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-smoothing:antialiased}html,button,input,optgroup,select,textarea{font-family:var(--base-font-family)}button,input,optgroup,select,textarea{font-size:100%;margin:0}a{text-decoration:none;-webkit-text-decoration-skip:ink;text-decoration-skip-ink:auto}body{margin:0}hr{height:0;margin:2em 0;border:none;border-bottom:var(--hr-border, 0)}img{border:0}main{display:block}main.hidden{display:none}mark{background:var(--mark-background);color:var(--mark-color)}pre{font-family:var(--pre-font-family);font-size:var(--pre-font-size);font-weight:var(--pre-font-weight);line-height:var(--pre-line-height)}small{display:inline-block;font-size:var(--small-font-size)}strong{font-weight:var(--strong-font-weight);color:var(--strong-color, currentColor)}sub,sup{font-size:var(--subsup-font-size);line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}body:not([data-platform^="Mac"]) *{scrollbar-color:hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.3) hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.1);scrollbar-width:thin}body:not([data-platform^="Mac"]) * ::-webkit-scrollbar{width:5px;height:5px}body:not([data-platform^="Mac"]) * ::-webkit-scrollbar-thumb{background:hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.3)}body:not([data-platform^="Mac"]) * ::-webkit-scrollbar-track{background:hsla(var(--mono-hue), var(--mono-saturation), 50%, 0.1)}::selection{background:var(--selection-color)}.emoji{height:var(--emoji-size);vertical-align:middle}.task-list-item{list-style:none}.task-list-item input{margin-right:0.5em;margin-left:0;vertical-align:0.075em}.markdown-section code[class*="lang-"],.markdown-section pre[data-lang]{font-family:var(--code-font-family);font-size:var(--code-font-size);font-weight:var(--code-font-weight);letter-spacing:normal;line-height:var(--code-block-line-height);-moz-tab-size:var(--code-tab-size);-o-tab-size:var(--code-tab-size);tab-size:var(--code-tab-size);text-align:left;white-space:pre;word-spacing:normal;word-wrap:normal;word-break:normal;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}.markdown-section pre[data-lang]{position:relative;overflow:hidden;margin:var(--code-block-margin);padding:0;border-radius:var(--code-block-border-radius)}.markdown-section pre[data-lang]::after{content:attr(data-lang);position:absolute;top:0.75em;right:0.75em;opacity:0.6;color:inherit;font-size:var(--font-size-s);line-height:1}.markdown-section pre[data-lang] code{display:block;overflow:auto;padding:var(--code-block-padding)}code[class*="lang-"],pre[data-lang]{color:var(--code-theme-text)}pre[data-lang]::selection,pre[data-lang] ::selection,code[class*="lang-"]::selection,code[class*="lang-"] ::selection{background:var(--code-theme-selection, var(--selection-color))}:not(pre)>code[class*="lang-"],pre[data-lang]{background:var(--code-theme-background)}.namespace{opacity:0.7}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:var(--code-theme-comment)}.token.punctuation{color:var(--code-theme-punctuation)}.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.deleted{color:var(--code-theme-tag)}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{color:var(--code-theme-selector)}.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string{color:var(--code-theme-operator)}.token.atrule,.token.attr-value,.token.keyword{color:var(--code-theme-keyword)}.token.function{color:var(--code-theme-function)}.token.regex,.token.important,.token.variable{color:var(--code-theme-variable)}.token.important,.token.bold{font-weight:bold}.token.italic{font-style:italic}.token.entity{cursor:help}.markdown-section{position:relative;max-width:var(--content-max-width);margin:0 auto;padding:2rem 45px}.app-nav:not(:empty) ~ main .markdown-section{padding-top:3.5rem}.markdown-section figure,.markdown-section p,.markdown-section ol,.markdown-section ul{margin:1em 0}.markdown-section ol,.markdown-section ul{padding-left:1.5rem}.markdown-section ol ol,.markdown-section ol ul,.markdown-section ul ol,.markdown-section ul ul{margin-top:0.15rem;margin-bottom:0.15rem}.markdown-section a{border-bottom:var(--link-border-bottom);color:var(--link-color);-webkit-text-decoration:var(--link-text-decoration);text-decoration:var(--link-text-decoration);-webkit-text-decoration-color:var(--link-text-decoration-color);text-decoration-color:var(--link-text-decoration-color)}.markdown-section a:hover{border-bottom:var(--link-border-bottom--hover, var(--link-border-bottom, 0));color:var(--link-color--hover, var(--link-color));-webkit-text-decoration:var(--link-text-decoration--hover, var(--link-text-decoration));text-decoration:var(--link-text-decoration--hover, var(--link-text-decoration));-webkit-text-decoration-color:var(--link-text-decoration-color--hover, var(--link-text-decoration-color));text-decoration-color:var(--link-text-decoration-color--hover, var(--link-text-decoration-color))}.markdown-section a.anchor{border-bottom:0;color:inherit;text-decoration:none}.markdown-section a.anchor:hover{text-decoration:underline}.markdown-section blockquote{overflow:visible;margin:2em 0;padding:1.5em;border-width:var(--blockquote-border-width, 0);border-style:var(--blockquote-border-style);border-color:var(--blockquote-border-color);border-radius:var(--blockquote-border-radius);background:var(--blockquote-background);color:var(--blockquote-color);font-family:var(--blockquote-font-family);font-size:var(--blockquote-font-size);font-style:var(--blockquote-font-style);font-weight:var(--blockquote-font-weight);quotes:"“" "”" "‘" "’"}.markdown-section blockquote em{font-family:var(--blockquote-em-font-family);font-size:var(--blockquote-em-font-size);font-style:var(--blockquote-em-font-style);font-weight:var(--blockquote-em-font-weight)}.markdown-section blockquote p:first-child{margin-top:0}.markdown-section blockquote p:first-child:before,.markdown-section blockquote p:first-child:after{color:var(--blockquote-quotes-color);font-family:var(--blockquote-quotes-font-family);font-size:var(--blockquote-quotes-font-size);line-height:0}.markdown-section blockquote p:first-child:before{content:var(--blockquote-quotes-open);margin-right:0.15em;vertical-align:-0.45em}.markdown-section blockquote p:first-child:after{content:var(--blockquote-quotes-close);margin-left:0.15em;vertical-align:-0.55em}.markdown-section blockquote p:last-child{margin-bottom:0}.markdown-section code{font-family:var(--code-font-family);font-size:var(--code-font-size);font-weight:var(--code-font-weight);line-height:inherit}.markdown-section code:not([class*="lang-"]):not([class*="language-"]){margin:var(--code-inline-margin);padding:var(--code-inline-padding);border-radius:var(--code-inline-border-radius);background:var(--code-inline-background);color:var(--code-inline-color, currentColor);white-space:nowrap}.markdown-section h1:first-child,.markdown-section h2:first-child,.markdown-section h3:first-child,.markdown-section h4:first-child,.markdown-section h5:first-child,.markdown-section h6:first-child{margin-top:0}.markdown-section h1+h2,.markdown-section h1+h3,.markdown-section h1+h4,.markdown-section h1+h5,.markdown-section h1+h6,.markdown-section h2+h3,.markdown-section h2+h4,.markdown-section h2+h5,.markdown-section h2+h6,.markdown-section h3+h4,.markdown-section h3+h5,.markdown-section h3+h6,.markdown-section h4+h5,.markdown-section h4+h6,.markdown-section h5+h6{margin-top:1rem}.markdown-section h1{margin:var(--heading-h1-margin, var(--heading-margin));padding:var(--heading-h1-padding, var(--heading-padding));border-width:var(--heading-h1-border-width, 0);border-style:var(--heading-h1-border-style);border-color:var(--heading-h1-border-color);font-family:var(--heading-h1-font-family, var(--heading-font-family));font-size:var(--heading-h1-font-size);font-weight:var(--heading-h1-font-weight, var(--heading-font-weight));line-height:var(--base-line-height);color:var(--heading-h1-color, var(--heading-color))}.markdown-section h2{margin:var(--heading-h2-margin, var(--heading-margin));padding:var(--heading-h2-padding, var(--heading-padding));border-width:var(--heading-h2-border-width, 0);border-style:var(--heading-h2-border-style);border-color:var(--heading-h2-border-color);font-family:var(--heading-h2-font-family, var(--heading-font-family));font-size:var(--heading-h2-font-size);font-weight:var(--heading-h2-font-weight, var(--heading-font-weight));line-height:var(--base-line-height);color:var(--heading-h2-color, var(--heading-color))}.markdown-section h3{margin:var(--heading-h3-margin, var(--heading-margin));padding:var(--heading-h3-padding, var(--heading-padding));border-width:var(--heading-h3-border-width, 0);border-style:var(--heading-h3-border-style);border-color:var(--heading-h3-border-color);font-family:var(--heading-h3-font-family, var(--heading-font-family));font-size:var(--heading-h3-font-size);font-weight:var(--heading-h3-font-weight, var(--heading-font-weight));color:var(--heading-h3-color, var(--heading-color))}.markdown-section h4{margin:var(--heading-h4-margin, var(--heading-margin));padding:var(--heading-h4-padding, var(--heading-padding));border-width:var(--heading-h4-border-width, 0);border-style:var(--heading-h4-border-style);border-color:var(--heading-h4-border-color);font-family:var(--heading-h4-font-family, var(--heading-font-family));font-size:var(--heading-h4-font-size);font-weight:var(--heading-h4-font-weight, var(--heading-font-weight));color:var(--heading-h4-color, var(--heading-color))}.markdown-section h5{margin:var(--heading-h5-margin, var(--heading-margin));padding:var(--heading-h5-padding, var(--heading-padding));border-width:var(--heading-h5-border-width, 0);border-style:var(--heading-h5-border-style);border-color:var(--heading-h5-border-color);font-family:var(--heading-h5-font-family, var(--heading-font-family));font-size:var(--heading-h5-font-size);font-weight:var(--heading-h5-font-weight, var(--heading-font-weight));color:var(--heading-h5-color, var(--heading-color))}.markdown-section h6{margin:var(--heading-h6-margin, var(--heading-margin));padding:var(--heading-h6-padding, var(--heading-padding));border-width:var(--heading-h6-border-width, 0);border-style:var(--heading-h6-border-style);border-color:var(--heading-h6-border-color);font-family:var(--heading-h6-font-family, var(--heading-font-family));font-size:var(--heading-h6-font-size);font-weight:var(--heading-h6-font-weight, var(--heading-font-weight));color:var(--heading-h6-color, var(--heading-color))}.markdown-section iframe{margin:1em 0}.markdown-section img{max-width:100%}.markdown-section kbd{display:inline-block;min-width:var(--kbd-min-width);margin:var(--kbd-margin);padding:var(--kbd-padding);border:var(--kbd-border);border-radius:var(--kbd-border-radius);background:var(--kbd-background);font-family:inherit;font-size:var(--kbd-font-size);text-align:center;letter-spacing:0;line-height:1;color:var(--kbd-color)}.markdown-section kbd+kbd{margin-left:-0.15em}.markdown-section table{display:block;overflow:auto;margin:1rem 0;border-spacing:0;border-collapse:collapse}.markdown-section th,.markdown-section td{padding:var(--table-cell-padding)}.markdown-section th:not([align]){text-align:left}.markdown-section thead{border-color:var(--table-head-border-color);border-style:solid;border-width:var(--table-head-border-width, 0);background:var(--table-head-background)}.markdown-section th{font-weight:var(--table-head-font-weight);color:var(--strong-color)}.markdown-section td{border-color:var(--table-cell-border-color);border-style:solid;border-width:var(--table-cell-border-width, 0)}.markdown-section tbody{border-color:var(--table-body-border-color);border-style:solid;border-width:var(--table-body-border-width, 0)}.markdown-section tbody tr:nth-child(odd){background:var(--table-row-odd-background)}.markdown-section tbody tr:nth-child(even){background:var(--table-row-even-background)}.markdown-section>ul .task-list-item{margin-left:-1.25em}.markdown-section>ul .task-list-item .task-list-item{margin-left:0}.markdown-section .table-wrapper table{display:table;width:100%}.markdown-section .table-wrapper td::before{display:none}@media (max-width: 30em){.markdown-section .table-wrapper tbody,.markdown-section .table-wrapper tr,.markdown-section .table-wrapper td{display:block}.markdown-section .table-wrapper th,.markdown-section .table-wrapper td{border:none}.markdown-section .table-wrapper thead{display:none}.markdown-section .table-wrapper tr{border-color:var(--table-cell-border-color);border-style:solid;border-width:var(--table-cell-border-width, 0);padding:var(--table-cell-padding)}.markdown-section .table-wrapper tr:not(:last-child){border-bottom:0}.markdown-section .table-wrapper td{display:flex;padding:0.15em 0}.markdown-section .table-wrapper td::before{display:block;min-width:8em;max-width:8em;font-weight:bold;text-align:left}}.markdown-section .tip,.markdown-section .warn{position:relative;margin:2em 0;padding:var(--notice-padding);border-width:var(--notice-border-width, 0);border-style:var(--notice-border-style);border-color:var(--notice-border-color);border-radius:var(--notice-border-radius);background:var(--notice-background);font-family:var(--notice-font-family);font-weight:var(--notice-font-weight);color:var(--notice-color)}.markdown-section .tip:before,.markdown-section .warn:before{display:inline-block;position:var(--notice-before-position, relative);top:var(--notice-before-top);left:var(--notice-before-left);height:var(--notice-before-height);width:var(--notice-before-width);margin:var(--notice-before-margin);padding:var(--notice-before-padding);border-radius:var(--notice-before-border-radius);line-height:var(--notice-before-line-height);font-family:var(--notice-before-font-family);font-size:var(--notice-before-font-size);font-weight:var(--notice-before-font-weight);text-align:center}.markdown-section .tip{border-width:var(--notice-important-border-width, var(--notice-border-width, 0));border-style:var(--notice-important-border-style, var(--notice-border-style));border-color:var(--notice-important-border-color, var(--notice-border-color));background:var(--notice-important-background, var(--notice-background));color:var(--notice-important-color, var(--notice-color))}.markdown-section .tip:before{content:var(--notice-important-before-content, var(--notice-before-content));background:var(--notice-important-before-background, var(--notice-before-background));color:var(--notice-important-before-color, var(--notice-before-color))}.markdown-section .warn{border-width:var(--notice-tip-border-width, var(--notice-border-width, 0));border-style:var(--notice-tip-border-style, var(--notice-border-style));border-color:var(--notice-tip-border-color, var(--notice-border-color));background:var(--notice-tip-background, var(--notice-background));color:var(--notice-tip-color, var(--notice-color))}.markdown-section .warn:before{content:var(--notice-tip-before-content, var(--notice-before-content));background:var(--notice-tip-before-background, var(--notice-before-background));color:var(--notice-tip-before-color, var(--notice-before-color))}.cover{display:none;position:relative;z-index:20;min-height:100vh;flex-direction:column;align-items:center;justify-content:center;padding:calc(var(--cover-border-inset, 0px) + var(--cover-border-width, 0px));color:var(--cover-color);text-align:var(--cover-text-align)}@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none){.cover{height:100vh}}.cover:before,.cover:after{content:'';position:absolute}.cover:before{top:0;bottom:0;left:0;right:0;background-blend-mode:var(--cover-background-blend-mode);background-color:var(--cover-background-color);background-image:var(--cover-background-image);background-position:var(--cover-background-position);background-repeat:var(--cover-background-repeat);background-size:var(--cover-background-size)}.cover:after{top:var(--cover-border-inset, 0);bottom:var(--cover-border-inset, 0);left:var(--cover-border-inset, 0);right:var(--cover-border-inset, 0);border-width:var(--cover-border-width, 0);border-style:solid;border-color:var(--cover-border-color)}.cover a{border-bottom:var(--cover-link-border-bottom);color:var(--cover-link-color);-webkit-text-decoration:var(--cover-link-text-decoration);text-decoration:var(--cover-link-text-decoration);-webkit-text-decoration-color:var(--cover-link-text-decoration-color);text-decoration-color:var(--cover-link-text-decoration-color)}.cover a:hover{border-bottom:var(--cover-link-border-bottom--hover, var(--cover-link-border-bottom));color:var(--cover-link-color--hover, var(--cover-link-color));-webkit-text-decoration:var(--cover-link-text-decoration--hover, var(--cover-link-text-decoration));text-decoration:var(--cover-link-text-decoration--hover, var(--cover-link-text-decoration));-webkit-text-decoration-color:var(--cover-link-text-decoration-color--hover, var(--cover-link-text-decoration-color));text-decoration-color:var(--cover-link-text-decoration-color--hover, var(--cover-link-text-decoration-color))}.cover h1{color:var(--cover-heading-color);position:relative;margin:0;font-size:var(--cover-heading-font-size);font-weight:var(--cover-heading-font-weight);line-height:1.2}.cover h1 a,.cover h1 a:hover{display:block;border-bottom:none;color:inherit;text-decoration:none}.cover h1 small{position:absolute;bottom:0;margin-left:0.5em}.cover h1 span{font-size:calc(var(--cover-heading-font-size-min) * 1px)}@media (min-width: 26em){.cover h1 span{font-size:calc((var(--cover-heading-font-size-min) * 1px) + (var(--cover-heading-font-size-max) - var(--cover-heading-font-size-min)) * ((100vw - 420px) / (1024 - 420)))}}@media (min-width: 64em){.cover h1 span{font-size:calc(var(--cover-heading-font-size-max) * 1px)}}.cover blockquote{margin:0;color:var(--cover-blockquote-color);font-size:var(--cover-blockquote-font-size)}.cover blockquote a{color:inherit}.cover ul{padding:0;list-style-type:none}.cover .cover-main{position:relative;z-index:1;max-width:var(--cover-max-width);margin:var(--cover-margin);padding:0 45px}.cover .cover-main>p:last-child{margin:1.25em -.25em}.cover .cover-main>p:last-child a{display:block;margin:.375em .25em;padding:var(--cover-button-padding);border:var(--cover-button-border);border-radius:var(--cover-button-border-radius);box-shadow:var(--cover-button-box-shadow);background:var(--cover-button-background);text-align:center;-webkit-text-decoration:var(--cover-button-text-decoration);text-decoration:var(--cover-button-text-decoration);-webkit-text-decoration-color:var(--cover-button-text-decoration-color);text-decoration-color:var(--cover-button-text-decoration-color);color:var(--cover-button-color);white-space:nowrap;transition:var(--cover-button-transition)}.cover .cover-main>p:last-child a:hover{border:var(--cover-button-border--hover, var(--cover-button-border));box-shadow:var(--cover-button-box-shadow--hover, var(--cover-button-box-shadow));background:var(--cover-button-background--hover, var(--cover-button-background));-webkit-text-decoration:var(--cover-button-text-decoration--hover, var(--cover-button-text-decoration));text-decoration:var(--cover-button-text-decoration--hover, var(--cover-button-text-decoration));-webkit-text-decoration-color:var(--cover-button-text-decoration-color--hover, var(--cover-button-text-decoration-color));text-decoration-color:var(--cover-button-text-decoration-color--hover, var(--cover-button-text-decoration-color));color:var(--cover-button-color--hover, var(--cover-button-color))}.cover .cover-main>p:last-child a:first-child{border:var(--cover-button-primary-border, var(--cover-button-border));box-shadow:var(--cover-button-primary-box-shadow, var(--cover-button-box-shadow));background:var(--cover-button-primary-background, var(--cover-button-background));-webkit-text-decoration:var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration));text-decoration:var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration));-webkit-text-decoration-color:var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color));text-decoration-color:var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color));color:var(--cover-button-primary-color, var(--cover-button-color))}.cover .cover-main>p:last-child a:first-child:hover{border:var(--cover-button-primary-border--hover, var(--cover-button-border--hover, var(--cover-button-primary-border, var(--cover-button-border))));box-shadow:var(--cover-button-primary-box-shadow--hover, var(--cover-button-box-shadow--hover, var(--cover-button-primary-box-shadow, var(--cover-button-box-shadow))));background:var(--cover-button-primary-background--hover, var(--cover-button-background--hover, var(--cover-button-primary-background, var(--cover-button-background))));-webkit-text-decoration:var(--cover-button-primary-text-decoration--hover, var(--cover-button-text-decoration--hover, var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration))));text-decoration:var(--cover-button-primary-text-decoration--hover, var(--cover-button-text-decoration--hover, var(--cover-button-primary-text-decoration, var(--cover-button-text-decoration))));-webkit-text-decoration-color:var(--cover-button-primary-text-decoration-color--hover, var(--cover-button-text-decoration-color--hover, var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color))));text-decoration-color:var(--cover-button-primary-text-decoration-color--hover, var(--cover-button-text-decoration-color--hover, var(--cover-button-primary-text-decoration-color, var(--cover-button-text-decoration-color))));color:var(--cover-button-primary-color--hover, var(--cover-button-color--hover, var(--cover-button-primary-color, var(--cover-button-color))))}@media (min-width: 30.01em){.cover .cover-main>p:last-child a{display:inline-block}}.cover .mask{visibility:var(--cover-background-mask-visibility, hidden);position:absolute;top:0;bottom:0;left:0;right:0;background-color:var(--cover-background-mask-color);opacity:var(--cover-background-mask-opacity)}.cover.has-mask .mask{visibility:visible}.cover.show{display:flex}.app-nav{position:absolute;z-index:30;top:calc(35px - (0.5em * var(--base-line-height)));left:45px;right:80px;text-align:right}.app-nav.no-badge{right:45px}.app-nav li>img,.app-nav li>a>img{margin-top:-0.25em;vertical-align:middle}.app-nav li>img:first-child,.app-nav li>a>img:first-child{margin-right:0.5em}.app-nav ul,.app-nav li{margin:0;padding:0;list-style:none}.app-nav li{position:relative}.app-nav li a{display:block;line-height:1;transition:var(--navbar-root-transition)}.app-nav>ul>li{display:inline-block;margin:var(--navbar-root-margin)}.app-nav>ul>li:first-child{margin-left:0}.app-nav>ul>li:last-child{margin-right:0}.app-nav>ul>li>a,.app-nav>ul>li>span{padding:var(--navbar-root-padding);border-width:var(--navbar-root-border-width, 0);border-style:var(--navbar-root-border-style);border-color:var(--navbar-root-border-color);border-radius:var(--navbar-root-border-radius);background:var(--navbar-root-background);color:var(--navbar-root-color);-webkit-text-decoration:var(--navbar-root-text-decoration);text-decoration:var(--navbar-root-text-decoration);-webkit-text-decoration-color:var(--navbar-root-text-decoration-color);text-decoration-color:var(--navbar-root-text-decoration-color)}.app-nav>ul>li>a:hover,.app-nav>ul>li>span:hover{background:var(--navbar-root-background--hover, var(--navbar-root-background));border-style:var(--navbar-root-border-style--hover, var(--navbar-root-border-style));border-color:var(--navbar-root-border-color--hover, var(--navbar-root-border-color));color:var(--navbar-root-color--hover, var(--navbar-root-color));-webkit-text-decoration:var(--navbar-root-text-decoration--hover, var(--navbar-root-text-decoration));text-decoration:var(--navbar-root-text-decoration--hover, var(--navbar-root-text-decoration));-webkit-text-decoration-color:var(--navbar-root-text-decoration-color--hover, var(--navbar-root-text-decoration-color));text-decoration-color:var(--navbar-root-text-decoration-color--hover, var(--navbar-root-text-decoration-color))}.app-nav>ul>li>a:not(:last-child),.app-nav>ul>li>span:not(:last-child){padding:var(--navbar-menu-root-padding, var(--navbar-root-padding));background:var(--navbar-menu-root-background, var(--navbar-root-background))}.app-nav>ul>li>a:not(:last-child):hover,.app-nav>ul>li>span:not(:last-child):hover{background:var(--navbar-menu-root-background--hover, var(--navbar-menu-root-background, var(--navbar-root-background--hover, var(--navbar-root-background))))}.app-nav>ul>li>a.active{background:var(--navbar-root-background--active, var(--navbar-root-background));border-style:var(--navbar-root-border-style--active, var(--navbar-root-border-style));border-color:var(--navbar-root-border-color--active, var(--navbar-root-border-color));color:var(--navbar-root-color--active, var(--navbar-root-color));-webkit-text-decoration:var(--navbar-root-text-decoration--active, var(--navbar-root-text-decoration));text-decoration:var(--navbar-root-text-decoration--active, var(--navbar-root-text-decoration));-webkit-text-decoration-color:var(--navbar-root-text-decoration-color--active, var(--navbar-root-text-decoration-color));text-decoration-color:var(--navbar-root-text-decoration-color--active, var(--navbar-root-text-decoration-color))}.app-nav>ul>li>a.active:not(:last-child):hover{background:var(--navbar-menu-root-background--active, var(--navbar-menu-root-background, var(--navbar-root-background--active, var(--navbar-root-background))))}.app-nav>ul>li ul{visibility:hidden;position:absolute;top:100%;right:50%;overflow-y:auto;box-sizing:border-box;max-height:calc(50vh);padding:var(--navbar-menu-padding);border-width:var(--navbar-menu-border-width, 0);border-style:solid;border-color:var(--navbar-menu-border-color);border-radius:var(--navbar-menu-border-radius);background:var(--navbar-menu-background);box-shadow:var(--navbar-menu-box-shadow);text-align:left;white-space:nowrap;opacity:0;-webkit-transform:translate(50%, -0.35em);transform:translate(50%, -0.35em);transition:var(--navbar-menu-transition)}.app-nav>ul>li ul li{white-space:nowrap}.app-nav>ul>li ul a{margin:var(--navbar-menu-link-margin);padding:var(--navbar-menu-link-padding);border-width:var(--navbar-menu-link-border-width, 0);border-style:var(--navbar-menu-link-border-style);border-color:var(--navbar-menu-link-border-color);border-radius:var(--navbar-menu-link-border-radius);background:var(--navbar-menu-link-background);color:var(--navbar-menu-link-color);-webkit-text-decoration:var(--navbar-menu-link-text-decoration);text-decoration:var(--navbar-menu-link-text-decoration);-webkit-text-decoration-color:var(--navbar-menu-link-text-decoration-color);text-decoration-color:var(--navbar-menu-link-text-decoration-color)}.app-nav>ul>li ul a:hover{background:var(--navbar-menu-link-background--hover, var(--navbar-menu-link-background));border-style:var(--navbar-menu-link-border-style--hover, var(--navbar-menu-link-border-style));border-color:var(--navbar-menu-link-border-color--hover, var(--navbar-menu-link-border-color));color:var(--navbar-menu-link-color--hover, var(--navbar-menu-link-color));-webkit-text-decoration:var(--navbar-menu-link-text-decoration--hover, var(--navbar-menu-link-text-decoration));text-decoration:var(--navbar-menu-link-text-decoration--hover, var(--navbar-menu-link-text-decoration));-webkit-text-decoration-color:var(--navbar-menu-link-text-decoration-color--hover, var(--navbar-menu-link-text-decoration-color));text-decoration-color:var(--navbar-menu-link-text-decoration-color--hover, var(--navbar-menu-link-text-decoration-color))}.app-nav>ul>li ul a.active{background:var(--navbar-menu-link-background--active, var(--navbar-menu-link-background));border-style:var(--navbar-menu-link-border-style--active, var(--navbar-menu-link-border-style));border-color:var(--navbar-menu-link-border-color--active, var(--navbar-menu-link-border-color));color:var(--navbar-menu-link-color--active, var(--navbar-menu-link-color));-webkit-text-decoration:var(--navbar-menu-link-text-decoration--active, var(--navbar-menu-link-text-decoration));text-decoration:var(--navbar-menu-link-text-decoration--active, var(--navbar-menu-link-text-decoration));-webkit-text-decoration-color:var(--navbar-menu-link-text-decoration-color--active, var(--navbar-menu-link-text-decoration-color));text-decoration-color:var(--navbar-menu-link-text-decoration-color--active, var(--navbar-menu-link-text-decoration-color))}.app-nav>ul>li:hover ul,.app-nav>ul>li:focus ul,.app-nav>ul>li.focus-within ul{visibility:visible;opacity:1;-webkit-transform:translate(50%, 0);transform:translate(50%, 0)}.sidebar,.sidebar-toggle{transition:transform var(--sidebar-transition-duration) ease-out}main>.content{transition:margin-left var(--sidebar-transition-duration) ease-out}@media (min-width: 48em){nav.app-nav{margin-left:var(--sidebar-width)}}main{position:relative;overflow-x:hidden;min-height:100vh}@media (min-width: 48em){main>.content{margin-left:var(--sidebar-width)}}.sidebar{display:flex;flex-direction:column;position:fixed;z-index:10;top:0;right:100%;overflow-x:hidden;overflow-y:auto;height:100vh;width:var(--sidebar-width);padding:var(--sidebar-padding);border-width:var(--sidebar-border-width);border-style:solid;border-color:var(--sidebar-border-color);background:var(--sidebar-background)}.sidebar>h1{margin:0;margin:var(--sidebar-name-margin);padding:var(--sidebar-name-padding);background:var(--sidebar-name-background);color:var(--sidebar-name-color);font-family:var(--sidebar-name-font-family);font-size:var(--sidebar-name-font-size);font-weight:var(--sidebar-name-font-weight);text-align:var(--sidebar-name-text-align)}.sidebar>h1 img{max-width:100%}.sidebar>h1 .app-name-link{color:var(--sidebar-name-color)}body:not([data-platform^="Mac"]) .sidebar::-webkit-scrollbar{width:5px}body:not([data-platform^="Mac"]) .sidebar::-webkit-scrollbar-thumb{border-radius:50vw}@media (min-width: 48em){.sidebar{position:absolute;-webkit-transform:translateX(var(--sidebar-width));transform:translateX(var(--sidebar-width))}}@media print{.sidebar{display:none}}.sidebar-nav,.sidebar nav{order:1;margin:var(--sidebar-nav-margin);padding:var(--sidebar-nav-padding);background:var(--sidebar-nav-background)}.sidebar-nav ul,.sidebar nav ul{margin:0;padding:0;list-style:none}.sidebar-nav ul ul,.sidebar nav ul ul{margin-left:var(--sidebar-nav-indent)}.sidebar-nav a,.sidebar nav a{display:block;overflow:hidden;margin:var(--sidebar-nav-link-margin);padding:var(--sidebar-nav-link-padding);border-width:var(--sidebar-nav-link-border-width, 0);border-style:var(--sidebar-nav-link-border-style);border-color:var(--sidebar-nav-link-border-color);border-radius:var(--sidebar-nav-link-border-radius);background-color:var(--sidebar-nav-link-background-color);background-image:var(--sidebar-nav-link-background-image);background-position:var(--sidebar-nav-link-background-position);background-repeat:var(--sidebar-nav-link-background-repeat);background-size:var(--sidebar-nav-link-background-size);color:var(--sidebar-nav-link-color);font-weight:var(--sidebar-nav-link-font-weight);white-space:nowrap;-webkit-text-decoration:var(--sidebar-nav-link-text-decoration);text-decoration:var(--sidebar-nav-link-text-decoration);-webkit-text-decoration-color:var(--sidebar-nav-link-text-decoration-color);text-decoration-color:var(--sidebar-nav-link-text-decoration-color);text-overflow:ellipsis;transition:var(--sidebar-nav-link-transition)}.sidebar-nav a img,.sidebar nav a img{margin-top:-0.25em;vertical-align:middle}.sidebar-nav a img:first-child,.sidebar nav a img:first-child{margin-right:0.5em}.sidebar-nav a:hover,.sidebar nav a:hover{border-width:var(--sidebar-nav-link-border-width--hover, var(--sidebar-nav-link-border-width, 0));border-style:var(--sidebar-nav-link-border-style--hover, var(--sidebar-nav-link-border-style));border-color:var(--sidebar-nav-link-border-color--hover, var(--sidebar-nav-link-border-color));background-color:var(--sidebar-nav-link-background-color--hover, var(--sidebar-nav-link-background-color));background-image:var(--sidebar-nav-link-background-image--hover, var(--sidebar-nav-link-background-image));background-position:var(--sidebar-nav-link-background-position--hover, var(--sidebar-nav-link-background-position));background-size:var(--sidebar-nav-link-background-size--hover, var(--sidebar-nav-link-background-size));color:var(--sidebar-nav-link-color--hover, var(--sidebar-nav-link-color));font-weight:var(--sidebar-nav-link-font-weight--hover, var(--sidebar-nav-link-font-weight));-webkit-text-decoration:var(--sidebar-nav-link-text-decoration--hover, var(--sidebar-nav-link-text-decoration));text-decoration:var(--sidebar-nav-link-text-decoration--hover, var(--sidebar-nav-link-text-decoration));-webkit-text-decoration-color:var(--sidebar-nav-link-text-decoration-color);text-decoration-color:var(--sidebar-nav-link-text-decoration-color)}.sidebar-nav ul>li>span,.sidebar-nav ul>li>strong,.sidebar nav ul>li>span,.sidebar nav ul>li>strong{display:block;margin:var(--sidebar-nav-strong-margin);padding:var(--sidebar-nav-strong-padding);border-width:var(--sidebar-nav-strong-border-width, 0);border-style:solid;border-color:var(--sidebar-nav-strong-border-color);color:var(--sidebar-nav-strong-color);font-size:var(--sidebar-nav-strong-font-size);font-weight:var(--sidebar-nav-strong-font-weight);text-transform:var(--sidebar-nav-strong-text-transform)}.sidebar-nav ul>li>span+ul,.sidebar-nav ul>li>strong+ul,.sidebar nav ul>li>span+ul,.sidebar nav ul>li>strong+ul{margin-left:0}.sidebar-nav ul>li:first-child>span,.sidebar-nav ul>li:first-child>strong,.sidebar nav ul>li:first-child>span,.sidebar nav ul>li:first-child>strong{margin-top:0}.sidebar-nav::-webkit-scrollbar,.sidebar nav::-webkit-scrollbar{width:0}@supports (width: env(safe-area-inset)){@media only screen and (orientation: landscape){.sidebar-nav,.sidebar nav{margin-left:calc(env(safe-area-inset-left) / 2)}}}.sidebar-nav li>a:before,.sidebar-nav li>strong:before{display:inline-block}.sidebar-nav li>a{background-repeat:var(--sidebar-nav-pagelink-background-repeat);background-size:var(--sidebar-nav-pagelink-background-size)}.sidebar-nav li>a:not([href*="?id="]){transition:var(--sidebar-nav-pagelink-transition)}.sidebar-nav li>a:not([href*="?id="]),.sidebar-nav li>a:not([href*="?id="]) ~ ul a{padding:var(--sidebar-nav-pagelink-padding, var(--sidebar-nav-link-padding))}.sidebar-nav li>a:not([href*="?id="]):only-child{background-image:var(--sidebar-nav-pagelink-background-image);background-position:var(--sidebar-nav-pagelink-background-position)}.sidebar-nav li>a:not([href*="?id="]):not(:only-child){background-image:var(--sidebar-nav-pagelink-background-image--loaded, var(--sidebar-nav-pagelink-background-image));background-position:var(--sidebar-nav-pagelink-background-position--loaded, var(--sidebar-nav-pagelink-background-image))}.sidebar-nav li.active>a,.sidebar-nav li.collapse>a{border-width:var(--sidebar-nav-link-border-width--active, var(--sidebar-nav-link-border-width));border-style:var(--sidebar-nav-link-border-style--active, var(--sidebar-nav-link-border-style));border-color:var(--sidebar-nav-link-border-color--active, var(--sidebar-nav-link-border-color));background-color:var(--sidebar-nav-link-background-color--active, var(--sidebar-nav-link-background-color));background-image:var(--sidebar-nav-link-background-image--active, var(--sidebar-nav-link-background-image));background-position:var(--sidebar-nav-link-background-position--active, var(--sidebar-nav-link-background-position));background-size:var(--sidebar-nav-link-background-size--active, var(--sidebar-nav-link-background-size));color:var(--sidebar-nav-link-color--active, var(--sidebar-nav-link-color));font-weight:var(--sidebar-nav-link-font-weight--active, var(--sidebar-nav-link-font-weight));-webkit-text-decoration:var(--sidebar-nav-link-text-decoration--active, var(--sidebar-nav-link-text-decoration));text-decoration:var(--sidebar-nav-link-text-decoration--active, var(--sidebar-nav-link-text-decoration));-webkit-text-decoration-color:var(--sidebar-nav-link-text-decoration-color);text-decoration-color:var(--sidebar-nav-link-text-decoration-color)}.sidebar-nav li.active>a:not([href*="?id="]):not(:only-child){background-image:var(--sidebar-nav-pagelink-background-image--active, var(--sidebar-nav-pagelink-background-image--loaded, var(--sidebar-nav-pagelink-background-image)));background-position:var(--sidebar-nav-pagelink-background-position--active, var(--sidebar-nav-pagelink-background-position--loaded, var(--sidebar-nav-pagelink-background-image)))}.sidebar-nav li.collapse>a:not([href*="?id="]):not(:only-child){background-image:var(--sidebar-nav-pagelink-background-image--collapse, var(--sidebar-nav-pagelink-background-image--loaded, var(--sidebar-nav-pagelink-background-image)));background-position:var(--sidebar-nav-pagelink-background-position--collapse, var(--sidebar-nav-pagelink-background-position--loaded, var(--sidebar-nav-pagelink-background-image)))}.sidebar-nav li.collapse .app-sub-sidebar{display:none}.sidebar-nav>ul>li>a:before{content:var(--sidebar-nav-link-before-content-l1, var(--sidebar-nav-link-before-content));margin:var(--sidebar-nav-link-before-margin-l1, var(--sidebar-nav-link-before-margin));color:var(--sidebar-nav-link-before-color-l1, var(--sidebar-nav-link-before-color))}.sidebar-nav>ul>li.active>a:before{content:var(--sidebar-nav-link-before-content-l1--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l1, var(--sidebar-nav-link-before-content))));color:var(--sidebar-nav-link-before-color-l1--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l1, var(--sidebar-nav-link-before-color))))}.sidebar-nav>ul>li>ul>li>a:before{content:var(--sidebar-nav-link-before-content-l2, var(--sidebar-nav-link-before-content));margin:var(--sidebar-nav-link-before-margin-l2, var(--sidebar-nav-link-before-margin));color:var(--sidebar-nav-link-before-color-l2, var(--sidebar-nav-link-before-color))}.sidebar-nav>ul>li>ul>li.active>a:before{content:var(--sidebar-nav-link-before-content-l2--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l2, var(--sidebar-nav-link-before-content))));color:var(--sidebar-nav-link-before-color-l2--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l2, var(--sidebar-nav-link-before-color))))}.sidebar-nav>ul>li>ul>li>ul>li>a:before{content:var(--sidebar-nav-link-before-content-l3, var(--sidebar-nav-link-before-content));margin:var(--sidebar-nav-link-before-margin-l3, var(--sidebar-nav-link-before-margin));color:var(--sidebar-nav-link-before-color-l3, var(--sidebar-nav-link-before-color))}.sidebar-nav>ul>li>ul>li>ul>li.active>a:before{content:var(--sidebar-nav-link-before-content-l3--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l3, var(--sidebar-nav-link-before-content))));color:var(--sidebar-nav-link-before-color-l3--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l3, var(--sidebar-nav-link-before-color))))}.sidebar-nav>ul>li>ul>li>ul>li>ul>li>a:before{content:var(--sidebar-nav-link-before-content-l4, var(--sidebar-nav-link-before-content));margin:var(--sidebar-nav-link-before-margin-l4, var(--sidebar-nav-link-before-margin));color:var(--sidebar-nav-link-before-color-l4, var(--sidebar-nav-link-before-color))}.sidebar-nav>ul>li>ul>li>ul>li>ul>li.active>a:before{content:var(--sidebar-nav-link-before-content-l4--active, var(--sidebar-nav-link-before-content--active, var(--sidebar-nav-link-before-content-l4, var(--sidebar-nav-link-before-content))));color:var(--sidebar-nav-link-before-color-l4--active, var(--sidebar-nav-link-before-color--active, var(--sidebar-nav-link-before-color-l4, var(--sidebar-nav-link-before-color))))}.sidebar-nav>:last-child{margin-bottom:2rem}.sidebar-toggle,.sidebar-toggle-button{width:var(--sidebar-toggle-width);outline:none}.sidebar-toggle{position:fixed;z-index:11;top:0;bottom:0;left:0;max-width:40px;margin:0;padding:0;border:0;background:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer}.sidebar-toggle .sidebar-toggle-button{position:absolute;top:var(--sidebar-toggle-offset-top);left:var(--sidebar-toggle-offset-left);height:var(--sidebar-toggle-height);border-radius:var(--sidebar-toggle-border-radius);border-width:var(--sidebar-toggle-border-width);border-style:var(--sidebar-toggle-border-style);border-color:var(--sidebar-toggle-border-color);background:var(--sidebar-toggle-background, transparent);color:var(--sidebar-toggle-icon-color)}.sidebar-toggle span{position:absolute;top:calc(50% - (var(--sidebar-toggle-icon-stroke-width) / 2));left:calc(50% - (var(--sidebar-toggle-icon-width) / 2));height:var(--sidebar-toggle-icon-stroke-width);width:var(--sidebar-toggle-icon-width);background-color:currentColor}.sidebar-toggle span:nth-child(1){margin-top:calc(0px - (var(--sidebar-toggle-icon-height) / 2))}.sidebar-toggle span:nth-child(3){margin-top:calc((var(--sidebar-toggle-icon-height) / 2))}@media (min-width: 48em){.sidebar-toggle{position:absolute;overflow:visible;top:var(--sidebar-toggle-offset-top);bottom:auto;left:0;height:var(--sidebar-toggle-height);-webkit-transform:translateX(var(--sidebar-width));transform:translateX(var(--sidebar-width))}.sidebar-toggle .sidebar-toggle-button{top:0}}@media print{.sidebar-toggle{display:none}}@media (max-width: 47.99em){body.close .sidebar,body.close .sidebar-toggle,body.close main>.content{-webkit-transform:translateX(var(--sidebar-width));transform:translateX(var(--sidebar-width))}}@media (min-width: 48em){body.close main>.content{-webkit-transform:translateX(0);transform:translateX(0)}}@media (max-width: 47.99em){body.close nav.app-nav,body.close .github-corner{display:none}}@media (min-width: 48em){body.close .sidebar,body.close .sidebar-toggle{-webkit-transform:translateX(0);transform:translateX(0)}}@media (min-width: 48em){body.close nav.app-nav{margin-left:0}}@media (max-width: 47.99em){body.close .sidebar-toggle{width:100%;max-width:none}body.close .sidebar-toggle span{margin-top:0}body.close .sidebar-toggle span:nth-child(1){-webkit-transform:rotate(45deg);transform:rotate(45deg)}body.close .sidebar-toggle span:nth-child(2){display:none}body.close .sidebar-toggle span:nth-child(3){-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}}@media (min-width: 48em){body.close main>.content{margin-left:0}}@media (min-width: 48em){body.sticky .sidebar,body.sticky .sidebar-toggle{position:fixed}}body .docsify-copy-code-button,body .docsify-copy-code-button:after{border-radius:var(--border-radius-m, 0);border-top-left-radius:0;border-bottom-right-radius:0;background:var(--copycode-background);color:var(--copycode-color)}body .docsify-copy-code-button span{border-radius:var(--border-radius-s, 0)}body .docsify-pagination-container{border-top:var(--pagination-border-top);color:var(--pagination-color)}body .pagination-item-label{font-size:var(--pagination-label-font-size)}body .pagination-item-label svg{color:var(--pagination-label-color);height:var(--pagination-chevron-height);stroke:var(--pagination-chevron-stroke);stroke-linecap:var(--pagination-chevron-stroke-linecap);stroke-linejoin:var(--pagination-chevron-stroke-linecap);stroke-width:var(--pagination-chevron-stroke-width)}body .pagination-item-title{color:var(--pagination-title-color);font-size:var(--pagination-title-font-size)}body .app-name.hide{display:block}body .sidebar{padding:var(--sidebar-padding)}.sidebar .search{margin:0;padding:0;border:0}.sidebar .search input{padding:0;line-height:1;font-size:inherit}.sidebar .search .clear-button{width:auto}.sidebar .search .clear-button svg{-webkit-transform:scale(1);transform:scale(1)}.sidebar .search .matching-post{border:none}.sidebar .search p{font-size:inherit}.sidebar .search{order:var(--search-flex-order);margin:var(--search-margin);padding:var(--search-padding);background:var(--search-background)}.sidebar .search a{color:inherit}.sidebar .search h2{margin:var(--search-result-heading-margin);font-size:var(--search-result-heading-font-size);font-weight:var(--search-result-heading-font-weight);color:var(--search-result-heading-color)}.sidebar .search .input-wrap{margin:var(--search-input-margin);background-color:var(--search-input-background-color);border-width:var(--search-input-border-width, 0);border-style:solid;border-color:var(--search-input-border-color);border-radius:var(--search-input-border-radius)}.sidebar .search input[type="search"]{min-width:0;padding:var(--search-input-padding);border:none;background-color:transparent;background-image:var(--search-input-background-image);background-position:var(--search-input-background-position);background-repeat:var(--search-input-background-repeat);background-size:var(--search-input-background-size);font-size:var(--search-input-font-size);color:var(--search-input-color);transition:var(--search-input-transition)}.sidebar .search input[type="search"]::-ms-clear{display:none}.sidebar .search input[type="search"]::-webkit-input-placeholder{color:var(--search-input-placeholder-color, gray)}.sidebar .search input[type="search"]:-ms-input-placeholder{color:var(--search-input-placeholder-color, gray)}.sidebar .search input[type="search"]::-ms-input-placeholder{color:var(--search-input-placeholder-color, gray)}.sidebar .search input[type="search"]::placeholder{color:var(--search-input-placeholder-color, gray)}.sidebar .search input[type="search"]::-webkit-input-placeholder{line-height:normal}.sidebar .search input[type="search"]:focus{background-color:var(--search-input-background-color--focus, var(--search-input-background-color));background-image:var(--search-input-background-image--focus, var(--search-input-background-image));background-position:var(--search-input-background-position--focus, var(--search-input-background-position));background-size:var(--search-input-background-size--focus, var(--search-input-background-size))}@supports (width: env(safe-area-inset)){@media only screen and (orientation: landscape){.sidebar .search input[type="search"]{margin-left:calc(env(safe-area-inset-left) / 2)}}}.sidebar .search p{overflow:hidden;text-overflow:ellipsis;-webkit-line-clamp:2}.sidebar .search p:empty{text-align:center}.sidebar .search .clear-button{margin:0 15px 0 0;padding:0;border:none;line-height:1;background:transparent;cursor:pointer}.sidebar .search .clear-button svg circle{fill:var(--search-clear-icon-color1, gray)}.sidebar .search .clear-button svg path{stroke:var(--search-clear-icon-color2, #fff)}.sidebar .search.show ~ *:not(h1){display:none}.sidebar .search .results-panel{display:none;color:var(--search-result-item-color);font-size:var(--search-result-item-font-size);font-weight:var(--search-result-item-font-weight)}.sidebar .search .results-panel.show{display:block}.sidebar .search .matching-post{margin:var(--search-result-item-margin);padding:var(--search-result-item-padding)}.sidebar .search .matching-post,.sidebar .search .matching-post:last-child{border-width:var(--search-result-item-border-width, 0) !important;border-style:var(--search-result-item-border-style);border-color:var(--search-result-item-border-color)}.sidebar .search .matching-post p{margin:0}.sidebar .search .search-keyword{margin:var(--search-result-keyword-margin);padding:var(--search-result-keyword-padding);border-radius:var(--search-result-keyword-border-radius);background-color:var(--search-result-keyword-background);color:var(--search-result-keyword-color, currentColor);font-style:normal;font-weight:var(--search-result-keyword-font-weight)}.medium-zoom-overlay,.medium-zoom-image--open{z-index:50 !important}.medium-zoom-overlay{background:var(--zoomimage-overlay-background) !important}:root{--mono-hue: 113;--mono-saturation: 0%;--mono-shade3: hsl(var(--mono-hue), var(--mono-saturation), 20%);--mono-shade2: hsl(var(--mono-hue), var(--mono-saturation), 30%);--mono-shade1: hsl(var(--mono-hue), var(--mono-saturation), 40%);--mono-base: hsl(var(--mono-hue), var(--mono-saturation), 50%);--mono-tint1: hsl(var(--mono-hue), var(--mono-saturation), 70%);--mono-tint2: hsl(var(--mono-hue), var(--mono-saturation), 89%);--mono-tint3: hsl(var(--mono-hue), var(--mono-saturation), 97%);--theme-hue: 204;--theme-saturation: 90%;--theme-lightness: 45%;--theme-color: hsl(var(--theme-hue), var(--theme-saturation), var(--theme-lightness));--modular-scale: 1.333;--modular-scale--2: calc(var(--modular-scale--1) / var(--modular-scale));--modular-scale--1: calc(var(--modular-scale-1) / var(--modular-scale));--modular-scale-1: 1rem;--modular-scale-2: calc(var(--modular-scale-1) * var(--modular-scale));--modular-scale-3: calc(var(--modular-scale-2) * var(--modular-scale));--modular-scale-4: calc(var(--modular-scale-3) * var(--modular-scale));--modular-scale-5: calc(var(--modular-scale-4) * var(--modular-scale));--font-size-xxxl: var(--modular-scale-5);--font-size-xxl: var(--modular-scale-4);--font-size-xl: var(--modular-scale-3);--font-size-l: var(--modular-scale-2);--font-size-m: var(--modular-scale-1);--font-size-s: var(--modular-scale--1);--font-size-xs: var(--modular-scale--2);--duration-slow: 1s;--duration-medium: 0.5s;--duration-fast: 0.25s;--spinner-size: 60px;--spinner-track-width: 4px;--spinner-track-color: rgba(0, 0, 0, 0.15);--spinner-transition-duration: var(--duration-medium)}:root{--base-background-color: #fff;--base-color: var(--mono-shade2);--base-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--base-font-size: 16px;--base-font-weight: normal;--base-line-height: 1.7;--emoji-size: calc(var(--base-line-height) * 1em);--hr-border: 1px solid var(--mono-tint2);--mark-background: #ffecb3;--pre-font-family: var(--code-font-family);--pre-font-size: var(--code-font-size);--pre-font-weight: normal;--selection-color: #b4d5fe;--small-font-size: var(--font-size-s);--strong-color: var(--heading-color);--strong-font-weight: 600;--subsup-font-size: var(--font-size-s)}:root{--content-max-width: 55em;--blockquote-background: var(--mono-tint3);--blockquote-border-style: solid;--blockquote-border-radius: var(--border-radius-m);--blockquote-em-font-weight: normal;--blockquote-font-weight: normal;--code-font-family: Inconsolata, Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono", "Lucida Console", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;--code-font-size: calc(var(--font-size-m) * 0.95);--code-font-weight: normal;--code-tab-size: 4;--code-block-border-radius: var(--border-radius-m);--code-block-line-height: var(--base-line-height);--code-block-margin: 1em 0;--code-block-padding: 1.75em 1.5em 1.5em 1.5em;--code-inline-background: var(--code-theme-background);--code-inline-border-radius: var(--border-radius-s);--code-inline-color: var(--code-theme-text);--code-inline-margin: 0 0.15em;--code-inline-padding: 0.125em 0.4em;--code-theme-background: var(--mono-tint3);--heading-color: var(--mono-shade3);--heading-margin: 2.5rem 0 0;--heading-h1-border-style: solid;--heading-h1-font-size: var(--font-size-xxl);--heading-h2-border-style: solid;--heading-h2-font-size: var(--font-size-xl);--heading-h3-border-style: solid;--heading-h3-font-size: var(--font-size-l);--heading-h4-border-style: solid;--heading-h4-font-size: var(--font-size-m);--heading-h5-border-style: solid;--heading-h5-font-size: var(--font-size-s);--heading-h6-border-style: solid;--heading-h6-font-size: var(--font-size-xs);--kbd-background: var(--mono-tint3);--kbd-border-radius: var(--border-radius-m);--kbd-margin: 0 0.3em;--kbd-min-width: 2.5em;--kbd-padding: 0.65em 0.5em;--link-text-decoration: underline;--notice-background: var(--mono-tint3);--notice-border-radius: var(--border-radius-m);--notice-border-style: solid;--notice-font-weight: normal;--notice-padding: 1em 1.5em;--notice-before-font-weight: normal;--table-cell-padding: 0.75em 0.5em;--table-head-border-color: var(--table-cell-border-color);--table-head-font-weight: var(--strong-font-weight);--table-row-odd-background: var(--mono-tint3)}:root{--cover-margin: 0 auto;--cover-max-width: 40em;--cover-text-align: center;--cover-background-color: var(--base-background-color);--cover-background-mask-color: var(--base-background-color);--cover-background-mask-opacity: 0.8;--cover-background-position: center center;--cover-background-repeat: no-repeat;--cover-background-size: cover;--cover-blockquote-font-size: var(--font-size-l);--cover-border-color: var(--theme-color);--cover-button-border: 1px solid var(--theme-color);--cover-button-border-radius: var(--border-radius-m);--cover-button-color: var(--theme-color);--cover-button-padding: 0.5em 2rem;--cover-button-text-decoration: none;--cover-button-transition: all var(--duration-fast) ease-in-out;--cover-button-primary-background: var(--theme-color);--cover-button-primary-border: 1px solid var(--theme-color);--cover-button-primary-color: #fff;--cover-heading-color: var(--theme-color);--cover-heading-font-size: var(--font-size-xxl);--cover-heading-font-weight: normal;--cover-link-text-decoration: underline }:root{--navbar-root-border-style: solid;--navbar-root-margin: 0 0 0 1.5em;--navbar-root-transition: all var(--duration-fast);--navbar-menu-background: var(--base-background-color);--navbar-menu-border-radius: var(--border-radius-m);--navbar-menu-box-shadow: rgba(45,45,45,0.05) 0px 0px 1px, rgba(49,49,49,0.05) 0px 1px 2px, rgba(42,42,42,0.05) 0px 2px 4px, rgba(32,32,32,0.05) 0px 4px 8px, rgba(49,49,49,0.05) 0px 8px 16px, rgba(35,35,35,0.05) 0px 16px 32px;--navbar-menu-padding: 0.5em;--navbar-menu-transition: all var(--duration-fast);--navbar-menu-link-border-style: solid;--navbar-menu-link-margin: 0.75em 0.5em;--navbar-menu-link-padding: 0.2em 0 }:root{--copycode-background: #808080;--copycode-color: #fff}:root{--docsifytabs-border-color: var(--mono-tint2);--docsifytabs-border-radius-px: var(--border-radius-s);--docsifytabs-tab-background: var(--mono-tint3);--docsifytabs-tab-color: var(--mono-tint1)}:root{--pagination-border-top: 1px solid var(--mono-tint2);--pagination-chevron-height: 0.8em;--pagination-chevron-stroke: currentColor;--pagination-chevron-stroke-linecap: round;--pagination-chevron-stroke-width: 1px;--pagination-label-font-size: var(--font-size-s);--pagination-title-font-size: var(--font-size-l)}:root{--search-margin: 1.5rem 0 0;--search-input-background-repeat: no-repeat;--search-input-border-color: var(--mono-tint1);--search-input-border-width: 1px;--search-input-padding: 0.5em;--search-flex-order: 1;--search-result-heading-color: var(--heading-color);--search-result-heading-font-size: var(--base-font-size);--search-result-heading-font-weight: normal;--search-result-heading-margin: 0 0 0.25em;--search-result-item-border-color: var(--mono-tint2);--search-result-item-border-style: solid;--search-result-item-border-width: 0 0 1px 0;--search-result-item-font-weight: normal;--search-result-item-padding: 1em 0;--search-result-keyword-background: var(--mark-background);--search-result-keyword-border-radius: var(--border-radius-s);--search-result-keyword-color: var(--mark-color);--search-result-keyword-font-weight: normal;--search-result-keyword-margin: 0 0.1em;--search-result-keyword-padding: 0.2em 0}:root{--zoomimage-overlay-background: rgba(0, 0, 0, 0.875)}:root{--sidebar-background: var(--base-background-color);--sidebar-border-width: 0;--sidebar-padding: 0 25px;--sidebar-transition-duration: var(--duration-fast);--sidebar-width: 17rem;--sidebar-name-font-size: var(--font-size-l);--sidebar-name-font-weight: normal;--sidebar-name-margin: 1.5rem 0 0;--sidebar-name-text-align: center;--sidebar-nav-strong-border-color: var(--sidebar-border-color);--sidebar-nav-strong-color: var(--heading-color);--sidebar-nav-strong-font-weight: var(--strong-font-weight);--sidebar-nav-strong-margin: 1.5em 0 0.5em;--sidebar-nav-strong-padding: 0.25em 0;--sidebar-nav-indent: 1em;--sidebar-nav-margin: 1.5rem 0 0;--sidebar-nav-link-border-style: solid;--sidebar-nav-link-border-width: 0;--sidebar-nav-link-color: var(--base-color);--sidebar-nav-link-font-weight: normal;--sidebar-nav-link-padding: 0.25em 0;--sidebar-nav-link-text-decoration--active: underline;--sidebar-nav-link-text-decoration--hover: underline;--sidebar-nav-link-before-margin: 0 0.35em 0 0;--sidebar-nav-pagelink-background-repeat: no-repeat;--sidebar-nav-pagelink-transition: var(--sidebar-nav-link-transition);--sidebar-toggle-border-radius: var(--border-radius-s);--sidebar-toggle-border-style: solid;--sidebar-toggle-border-width: 0;--sidebar-toggle-height: 36px;--sidebar-toggle-icon-color: var(--base-color);--sidebar-toggle-icon-height: 10px;--sidebar-toggle-icon-stroke-width: 1px;--sidebar-toggle-icon-width: 16px;--sidebar-toggle-offset-left: 0;--sidebar-toggle-offset-top: calc(35px - (var(--sidebar-toggle-height) / 2));--sidebar-toggle-width: 44px}:root{--code-theme-background: #f3f3f3;--code-theme-comment: #6e8090;--code-theme-function: #dd4a68;--code-theme-keyword: #07a;--code-theme-operator: #a67f59;--code-theme-punctuation: #999;--code-theme-selection: #b3d4fc;--code-theme-selector: #690;--code-theme-tag: #905;--code-theme-text: #333;--code-theme-variable: #e90}:root{--border-radius-s: 2px;--border-radius-m: 4px;--border-radius-l: 8px;--strong-font-weight: 600;--blockquote-border-color: var(--theme-color);--blockquote-border-radius: 0 var(--border-radius-m) var(--border-radius-m) 0;--blockquote-border-width: 0 0 0 4px;--code-inline-background: var(--mono-tint2);--code-theme-background: var(--mono-tint3);--heading-font-weight: var(--strong-font-weight);--heading-h1-font-weight: 400;--heading-h2-font-weight: 400;--heading-h2-border-color: var(--mono-tint2);--heading-h2-border-width: 0 0 1px 0;--heading-h2-margin: 2.5rem 0 1.5rem;--heading-h2-padding: 0 0 1rem 0;--kbd-border: 1px solid var(--mono-tint2);--notice-border-radius: 0 var(--border-radius-m) var(--border-radius-m) 0;--notice-border-width: 0 0 0 4px;--notice-padding: 1em 1.5em 1em 3em;--notice-before-border-radius: 100%;--notice-before-font-weight: bold;--notice-before-height: 1.5em;--notice-before-left: 0.75em;--notice-before-line-height: 1.5;--notice-before-margin: 0 0.25em 0 0;--notice-before-position: absolute;--notice-before-width: var(--notice-before-height);--notice-important-background: hsl(340, 60%, 96%);--notice-important-border-color: hsl(340, 90%, 45%);--notice-important-before-background: var(--notice-important-border-color) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3E%3Cpath d='M10 14C10 15.1 9.1 16 8 16 6.9 16 6 15.1 6 14 6 12.9 6.9 12 8 12 9.1 12 10 12.9 10 14Z'/%3E%3Cpath d='M10 1.6C10 1.2 9.8 0.9 9.6 0.7 9.2 0.3 8.6 0 8 0 7.4 0 6.8 0.2 6.5 0.6 6.2 0.9 6 1.2 6 1.6 6 1.7 6 1.8 6 1.9L6.8 9.6C6.9 9.9 7 10.1 7.2 10.2 7.4 10.4 7.7 10.5 8 10.5 8.3 10.5 8.6 10.4 8.8 10.3 9 10.1 9.1 9.9 9.2 9.6L10 1.9C10 1.8 10 1.7 10 1.6Z'/%3E%3C/svg%3E") center / 0.875em no-repeat;--notice-important-before-color: #fff;--notice-important-before-content: "";--notice-tip-background: hsl(204, 60%, 96%);--notice-tip-border-color: hsl(204, 90%, 45%);--notice-tip-before-background: var(--notice-tip-border-color) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3E%3Cpath d='M9.1 0C10.2 0 10.7 0.7 10.7 1.6 10.7 2.6 9.8 3.6 8.6 3.6 7.6 3.6 7 3 7 2 7 1.1 7.7 0 9.1 0Z'/%3E%3Cpath d='M5.8 16C5 16 4.4 15.5 5 13.2L5.9 9.1C6.1 8.5 6.1 8.2 5.9 8.2 5.7 8.2 4.6 8.6 3.9 9.1L3.5 8.4C5.6 6.6 7.9 5.6 8.9 5.6 9.8 5.6 9.9 6.6 9.5 8.2L8.4 12.5C8.2 13.2 8.3 13.5 8.5 13.5 8.7 13.5 9.6 13.2 10.4 12.5L10.9 13.2C8.9 15.2 6.7 16 5.8 16Z'/%3E%3C/svg%3E") center / 0.875em no-repeat;--notice-tip-before-color: #fff;--notice-tip-before-content: "";--table-cell-border-color: var(--mono-tint2);--table-cell-border-width: 1px 0;--cover-background-color: hsl(var(--theme-hue), 25%, 60%);--cover-background-image: radial-gradient(ellipse at center 115%, rgba(255, 255, 255, 0.9), transparent);--cover-blockquote-color: var(--strong-color);--cover-heading-color: #fff;--cover-heading-font-size-max: 56;--cover-heading-font-size-min: 34;--cover-heading-font-weight: 200;--navbar-root-color--active: var(--theme-color);--navbar-menu-border-radius: var(--border-radius-m);--navbar-menu-root-background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9.6' height='6' viewBox='0 0 9.6 6'%3E%3Cpath d='M1.5 1.5l3.3 3 3.3-3' stroke-width='1.5' stroke='rgb%28179, 179, 179%29' fill='none' stroke-linecap='square' stroke-linejoin='miter' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E") right no-repeat;--navbar-menu-root-padding: 0 18px 0 0;--search-input-background-color: #fff;--search-input-background-image: url("data:image/svg+xml,%3Csvg height='20px' width='20px' viewBox='0 0 24 24' fill='none' stroke='rgba(0, 0, 0, 0.3)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' preserveAspectRatio='xMidYMid meet' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='10.5' cy='10.5' r='7.5' vector-effect='non-scaling-stroke'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='15.8' y2='15.8' vector-effect='non-scaling-stroke'%3E%3C/line%3E%3C/svg%3E");--search-input-background-position: 21px center;--search-input-border-color: var(--sidebar-border-color);--search-input-border-width: 1px 0;--search-input-margin: 0 -25px;--search-input-padding: 0.65em 1em 0.65em 50px;--search-input-placeholder-color: rgba(0, 0, 0, 0.4);--search-clear-icon-color1: rgba(0, 0, 0, 0.3);--search-result-heading-font-weight: var(--strong-font-weight);--search-result-item-border-color: var(--sidebar-border-color);--search-result-keyword-border-radius: var(--border-radius-s);--sidebar-background: var(--mono-tint3);--sidebar-border-color: var(--mono-tint2);--sidebar-border-width: 0 1px 0 0;--sidebar-name-color: var(--theme-color);--sidebar-name-font-weight: 300;--sidebar-nav-strong-border-width: 0 0 1px 0;--sidebar-nav-strong-font-size: smaller;--sidebar-nav-strong-margin: 2em -25px 0.75em 0;--sidebar-nav-strong-padding: 0.25em 0 0.75em 0;--sidebar-nav-strong-text-transform: uppercase;--sidebar-nav-link-border-color: transparent;--sidebar-nav-link-border-color--active: var(--theme-color);--sidebar-nav-link-border-width: 0 4px 0 0;--sidebar-nav-link-color--active: var(--theme-color);--sidebar-nav-link-margin: 0 -25px 0 0;--sidebar-nav-link-text-decoration: none;--sidebar-nav-link-text-decoration--active: none;--sidebar-nav-link-text-decoration--hover: underline;--sidebar-nav-link-before-content-l3: '-';--sidebar-nav-pagelink-background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11.2' viewBox='0 0 7 11.2'%3E%3Cpath d='M1.5 1.5l4 4.1 -4 4.1' stroke-width='1.5' stroke='rgb%28179, 179, 179%29' fill='none' stroke-linecap='square' stroke-linejoin='miter' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");--sidebar-nav-pagelink-background-image--active: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11.2' height='7' viewBox='0 0 11.2 7'%3E%3Cpath d='M1.5 1.5l4.1 4 4.1-4' stroke-width='1.5' stroke='rgb%2811, 135, 218%29' fill='none' stroke-linecap='square' stroke-linejoin='miter' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");--sidebar-nav-pagelink-background-image--collapse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11.2' viewBox='0 0 7 11.2'%3E%3Cpath d='M1.5 1.5l4 4.1 -4 4.1' stroke-width='1.5' stroke='rgb%2811, 135, 218%29' fill='none' stroke-linecap='square' stroke-linejoin='miter' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");--sidebar-nav-pagelink-background-image--loaded: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11.2' height='7' viewBox='0 0 11.2 7'%3E%3Cpath d='M1.5 1.5l4.1 4 4.1-4' stroke-width='1.5' stroke='rgb%2811, 135, 218%29' fill='none' stroke-linecap='square' stroke-linejoin='miter' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E");--sidebar-nav-pagelink-background-position: 3px center;--sidebar-nav-pagelink-background-position--active: left center;--sidebar-nav-pagelink-background-position--collapse: var(--sidebar-nav-pagelink-background-position);--sidebar-nav-pagelink-background-position--loaded: var(--sidebar-nav-pagelink-background-position--active);--sidebar-nav-pagelink-padding: 0.25em 0 0.25em 20px;--sidebar-nav-pagelink-transition: none;--sidebar-toggle-background: var(--sidebar-border-color);--sidebar-toggle-border-radius: 0 var(--border-radius-s) var(--border-radius-s) 0;--sidebar-toggle-width: 32px}:root{--code-theme-background: #222;--code-theme-comment: #516e7a;--code-theme-function: #f07178;--code-theme-keyword: #c2e78c;--code-theme-operator: #ffcb6b;--code-theme-punctuation: #89ddff;--code-theme-selection: rgba(255, 255, 255, 0.2);--code-theme-selector: #ffcb6b;--code-theme-tag: #f07178;--code-theme-text: #f3f3f3;--code-theme-variable: #ffcb6b}:root{--mono-hue: 201;--mono-saturation: 18%;--mono-shade3: hsl(var(--mono-hue), var(--mono-saturation), 13%);--mono-shade2: hsl(var(--mono-hue), var(--mono-saturation), 15%);--mono-shade1: hsl(var(--mono-hue), var(--mono-saturation), 17%);--mono-base: hsl(var(--mono-hue), var(--mono-saturation), 19%);--mono-tint1: hsl(var(--mono-hue), var(--mono-saturation), 25%);--mono-tint2: hsl(var(--mono-hue), var(--mono-saturation), 35%);--mono-tint3: hsl(var(--mono-hue), var(--mono-saturation), 43%);--spinner-track-color: rgba(255, 255, 255, 0.15);--base-background-color: var(--mono-base);--base-color: #d3d3d3;--hr-border: 1px solid var(--mono-tint2);--mark-background: #ffcb6b;--mark-color: var(--base-background-color);--blockquote-background: var(--mono-shade2);--code-inline-background: var(--mono-tint1);--code-theme-background: var(--mono-shade2);--heading-color: #fff;--heading-h2-border-color: var(--mono-tint2);--kbd-background: var(--mono-shade2);--kbd-border: none;--kbd-color: var(--strong-color);--notice-important-background: var(--mono-shade2);--notice-tip-background: var(--mono-shade2);--table-cell-border-color: var(--mono-tint1);--table-row-odd-background: var(--mono-shade2);--cover-background-color: var(--base-background-color);--cover-background-image: radial-gradient(ellipse at center bottom, var(--mono-tint3), transparent);--cover-blockquote-color: var(--mark-background);--cover-button-border: 1px solid var(--mono-tint3);--cover-button-color: #fff;--navbar-menu-background: var(--mono-tint1);--navbar-menu-box-shadow: rgba(0,0,0,0.05) 0px 0px 1px, rgba(0,0,0,0.05) 0px 1px 2px, rgba(0,0,0,0.05) 0px 2px 4px, rgba(0,0,0,0.05) 0px 4px 8px, rgba(0,0,0,0.05) 0px 8px 16px, rgba(0,0,0,0.05) 0px 16px 32px;--copycode-background: var(--mono-tint1);--copycode-color: #fff;--docsifytabs-border-color: var(--mono-tint2);--docsifytabs-tab-background: var(--mono-shade1);--docsifytabs-tab-color: var(--mono-tint2);--pagination-border-top: 1px solid var(--mono-tint2);--pagination-title-color: #fff;--search-input-background-color: var(--mono-shade2);--search-input-background-image: url("data:image/svg+xml,%3Csvg height='20px' width='20px' viewBox='0 0 24 24' fill='none' stroke='rgba(255, 255, 255, 0.3)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' preserveAspectRatio='xMidYMid meet' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='10.5' cy='10.5' r='7.5' vector-effect='non-scaling-stroke'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='15.8' y2='15.8' vector-effect='non-scaling-stroke'%3E%3C/line%3E%3C/svg%3E");--search-input-border-color: var(--mono-tint1);--search-input-placeholder-color: rgba(255, 255, 255, 0.4);--search-clear-icon-color1: rgba(255, 255, 255, 0.3);--sidebar-background: var(--mono-shade1);--sidebar-border-color: var(--mono-tint1);--sidebar-nav-pagelink-background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='11.2' viewBox='0 0 7 11.2'%3E%3Cpath d='M1.5 1.5l4 4.1 -4 4.1' stroke-width='1.5' stroke='rgb%2873, 93, 104%29' fill='none' stroke-linecap='square' stroke-linejoin='miter' vector-effect='non-scaling-stroke'/%3E%3C/svg%3E")}
-/*# sourceMappingURL=theme-simple-dark.css.map */
diff --git a/docs/assets/twitter.svg b/docs/assets/twitter.svg
deleted file mode 100644
index b712371d64..0000000000
--- a/docs/assets/twitter.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/warning.svg b/docs/assets/warning.svg
deleted file mode 100644
index dcef83a537..0000000000
--- a/docs/assets/warning.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/website.svg b/docs/assets/website.svg
deleted file mode 100644
index a00aa3689b..0000000000
--- a/docs/assets/website.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/autoquality.md b/docs/autoquality.md
deleted file mode 100644
index f59a5f5879..0000000000
--- a/docs/autoquality.md
+++ /dev/null
@@ -1,127 +0,0 @@
-# Autoquality![pro](./assets/pro.svg)
-
-imgproxy can calculate quality for your resultant images so they best fit the selected metric. The supported methods are [none](#none), [size](#autoquality-by-file-size), [dssim](#autoquality-by-dssim), and [ml](#autoquality-with-ml).
-
-**⚠️ Warning:** Autoquality requires an image to be saved several times. Use it only when you prefer the resultant quality and size over speed.
-
-You can enable autoquality using [config options](configuration.md#autoquality) (for all images) or with [processing options](generating_the_url.md#autoquality) (for each image individually).
-
-## None
-
-Disable the autoquality.
-
-**Method name:** `none`
-
-#### Config example
-
-```bash
-IMGPROXY_AUTOQUALITY_METHOD="none"
-```
-
-#### Processing options example
-
-```
-.../autoquality:none/...
-```
-
-## Autoquality by file size
-
-With this method, imgproxy will try to degrade the quality so your image fits the desired file size.
-
-**Method name:** `size`
-
-**Target:** the desired file size
-
-#### Config example
-
-```bash
-IMGPROXY_AUTOQUALITY_METHOD="size"
-# Change value to the desired size in bytes
-IMGPROXY_AUTOQUALITY_TARGET=10240
-IMGPROXY_AUTOQUALITY_MIN=10
-IMGPROXY_AUTOQUALITY_MAX=80
-# Quality 50 for AVIF is pretty the same as 80 for JPEG
-IMGPROXY_AUTOQUALITY_FORMAT_MAX="avif=50"
-```
-
-#### Processing options example
-
-```
-.../autoquality:size:10240:10:80/...
-```
-
-## Autoquality by DSSIM
-
-With this method imgproxy will try to select a level of quality so that the saved image will have the desired [DSSIM](https://en.wikipedia.org/wiki/Structural_similarity#Structural_Dissimilarity) value.
-
-**Method name:** `dssim`
-
-**Target:** the desired DSSIM value
-
-#### Config example
-
-```bash
-IMGPROXY_AUTOQUALITY_METHOD="dssim"
-# Change value to the desired DSSIM
-IMGPROXY_AUTOQUALITY_TARGET=0.02
-# We're happy enough if the resulting DSSIM will differ from the desired by 0.001
-IMGPROXY_AUTOQUALITY_ALLOWED_ERROR=0.001
-IMGPROXY_AUTOQUALITY_MIN=70
-IMGPROXY_AUTOQUALITY_MAX=80
-# Quality 50 for AVIF is pretty the same as 80 for JPEG
-IMGPROXY_AUTOQUALITY_FORMAT_MIN="avif=40"
-IMGPROXY_AUTOQUALITY_FORMAT_MAX="avif=50"
-```
-
-#### Processing options example
-
-```
-.../autoquality:dssim:0.02:70:80:0.001/...
-```
-
-## Autoquality with ML
-
-This method is almost the same as autoquality with [DSSIM](#autoquality-by-dssim) but imgproxy will try to predict the initial quality using neural networks. This requires neural networks to be configured (see the config examlpe or the config documentation). If a neural network for the resulting format is not provided, the [DSSIM](#autoquality-by-dssim) method will be used instead.
-
-**📝 Note:** When this method is used, imgproxy will save JPEG images with the most optimal [advanced JPEG compression](configuration.md#advanced-jpeg-compression) settings, ignoring config and processing options.
-
-**Method name:** `ml`
-
-**Target:** the desired DSSIM value
-
-#### Config example
-
-```bash
-IMGPROXY_AUTOQUALITY_METHOD="ml"
-# Change value to the desired DSSIM
-IMGPROXY_AUTOQUALITY_TARGET=0.02
-# We're happy enough if the resulting DSSIM will differ from the desired by 0.001
-IMGPROXY_AUTOQUALITY_ALLOWED_ERROR=0.001
-IMGPROXY_AUTOQUALITY_MIN=70
-IMGPROXY_AUTOQUALITY_MAX=80
-# Quality 50 for AVIF is pretty the same as 80 for JPEG
-IMGPROXY_AUTOQUALITY_FORMAT_MIN="avif=40"
-IMGPROXY_AUTOQUALITY_FORMAT_MAX="avif=50"
-# Neural networks paths for JPEG, WebP, and AVIF
-IMGPROXY_AUTOQUALITY_JPEG_NET="/networks/autoquality-jpeg.pb"
-IMGPROXY_AUTOQUALITY_WEBP_NET="/networks/autoquality-webp.pb"
-IMGPROXY_AUTOQUALITY_AVIF_NET="/networks/autoquality-avif.pb"
-```
-
-**📝 Note:** If you trust your neural network's autoquality, you may want to set `IMGPROXY_AUTOQUALITY_ALLOWED_ERROR` to 1 (the maximum possible DSSIM value). In this case, imgproxy will always use the quality predicted by the neural network.
-
-#### Processing options example
-
-```
-.../autoquality:ml:0.02:70:80:0.001/...
-```
-
-### Neural networks format
-
-Neural networks should fit the following requirements:
-* Tensorflow frozen graph format
-* Input layer size is 416x416
-* Output layer size is 1x100
-* Output layer values are logits of quality probabilities
-
-If you're an imgproxy Pro user and you want to train your own network but you don't know how, feel free to contact the imgproxy team for instructions.
diff --git a/docs/best_format.md b/docs/best_format.md
deleted file mode 100644
index fc6cf27a22..0000000000
--- a/docs/best_format.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Best format![pro](./assets/pro.svg)
-
-You can use the `best` value for the [format](generating_the_url.md#format) option or the [extension](generating_the_url.md#extension) to make imgproxy pick the best format for the resultant image.
-
-imgproxy measures the complexity of the image to choose when it should use a lossless or near-lossless encoding. Then imgproxy tries to save the image in multiple formats to pick one with the smallest resulting size.
-
-**📝 Note:** imgproxy uses only the formats listed as [preferred](configuration.md#preferred-formats) when choosing the best format. It may also use AVIF or WebP if [AVIF/WebP support detection](configuration.md#avifwebp-support-detection) is enabled.
-
-**📝 Note:** imgproxy will use AVIF or WebP _only_ if [AVIF/WebP support detection](configuration.md#avifwebp-support-detection) is enabled.
-
-**📝 Note:** imgproxy may change your quality and autoquality settings if the `best` format is used.
-
-## Configuration
-
-* `IMGPROXY_BEST_FORMAT_COMPLEXITY_THRESHOLD `: the image complexity threshold. imgproxy will use a lossless or near-lossless encoding for images with low complexity. Default: `5.5`
-* `IMGPROXY_BEST_FORMAT_MAX_RESOLUTION`: when greater than `0` and the image's resolution (in megapixels) is larger than the provided value, imgproxy won't try all the applicable formats and will just pick one that seems the best for the image
-* `IMGPROXY_BEST_FORMAT_BY_DEFAULT`: when `true` and the resulting image format is not specified explicitly, imgproxy will use the `best` format instead of the source image format
-* `IMGPROXY_BEST_FORMAT_ALLOW_SKIPS`: when `true` and the `best` format is used, imgproxy will skip processing of SVG and formats [listed to skip processing](configuration.md#skip-processing)
diff --git a/docs/chained_pipelines.md b/docs/chained_pipelines.md
deleted file mode 100644
index 6a633a084a..0000000000
--- a/docs/chained_pipelines.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Chained pipelines![pro](./assets/pro.svg)
-
-Though imgproxy's [processing pipeline](about_processing_pipeline.md) is suitable for most cases, sometimes it's handy to run multiple chained pipelines with different options.
-
-imgproxy Pro allows you to start a new pipeline by inserting a section with a minus sign (`-`) to the URL path:
-
-```
-.../width:500/crop:1000/-/trim:10/...
- ^ the new pipeline starts here
-```
-
-### Example 1: Multiple watermarks
-
-If you need to place multiple watermarks on the same image, you can use chained pipelines for that:
-
-```
-.../rs:fit:500:500/wm:0.5:nowe/wmu:aW1hZ2UxCg/-/wm:0.7:soea/wmu:aW1hZ2UyCg/...
-```
-
-In this example, the first pipeline resizes the image and places the first watermark, and the second pipeline places the second watermark.
-
-### Example 2: Fast trim
-
-Performing the `trim` operation is pretty heavy as it involves loading the entire image into memory from the very start of processing. However, if you're going to scale down your image and trim accuracy is not very important to you, it's better to move trimming to a separate pipeline.
-
-```
-.../rs:fit:500:500/-/trim:10/...
-```
-
-In this example, the first pipeline resizes the image, and the second pipeline trims the result. Since the result of the first pipeline is already resized and loaded to the memory, trimming will be done much faster.
-
-## Using with presets
-
-You can use presets in your chained pipelines, and you can use chained pipelines in your presets. However, the behaior may be not obvious. The rules are the following:
-
-* A preset is applied to the pipeline where is was used.
-* A preset may contain a chained pipeline, and will be chained to the pipeline where the preset was used.
-* Chained pipelines from the preset and from the URL are merged.
-
-### Example
-
-If we have the following preset:
-
-```
-test=width:300/height:300/-/width:200/height:200/-/width:100/height:200
-```
-
-And the following URL:
-
-```
-.../width:400/-/preset:test/width:500/-/width:600/...
-```
-
-The result will look like this:
-
-```
-.../width:400/-/width:500/height:300/-/width:600/height:200/-/width:100/height:200/...
-```
diff --git a/docs/cloud_watch.md b/docs/cloud_watch.md
deleted file mode 100644
index 696e096e96..0000000000
--- a/docs/cloud_watch.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Amazon CloudWatch
-
-imgproxy can send its metrics to Amazon CloudWatch. To use this feature, do the following:
-
-1. Set the `IMGPROXY_CLOUD_WATCH_SERVICE_NAME` environment variable. imgproxy will use the value of this variable as a value for the `ServiceName` dimension.
-2. [Set up the necessary credentials](#set-up-credentials) to grant access to CloudWatch.
-3. _(optional)_ Specify the AWS region with `IMGPROXY_CLOUD_WATCH_REGION` or `AWS_REGION`. Default: `us-west-1`
-4. _(optional)_ Set the `IMGPROXY_CLOUD_WATCH_NAMESPACE` environment variable to be the desired CloudWatch namespace. Default: `imgproxy`
-
-imgproxy sends the following metrics to CloudWatch:
-
-* `RequestsInProgress`: the number of requests currently in progress
-* `ImagesInProgress`: the number of images currently in progress
-* `WorkersUtilization`, `ConcurrencyUtilization`: the percentage of imgproxy's workers utilization. Calculated as `RequestsInProgress / IMGPROXY_WORKERS * 100`
-* `BufferSize`: a summary of the download buffers sizes (in bytes)
-* `BufferDefaultSize`: calibrated default buffer size (in bytes)
-* `BufferMaxSize`: calibrated maximum buffer size (in bytes)
-* `VipsMemory`: libvips memory usage (in bytes)
-* `VipsMaxMemory`: libvips maximum memory usage (in bytes)
-* `VipsAllocs`: the number of active vips allocations
-
-### Set up credentials
-
-There are three ways to specify your AWS credentials. The credentials need to have rights to write metrics to CloudWatch:
-
-#### IAM Roles
-
-If you're running imgproxy on an Amazon Web Services platform, you can use IAM roles to to get the security credentials to make calls to AWS CloudWatch.
-
-* **Elastic Container Service (ECS):** Assign an [IAM role to a task](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html).
-* **Elastic Kubernetes Service (EKS):** Assign a [service account to a pod](https://docs.aws.amazon.com/eks/latest/userguide/pod-configuration.html).
-* **Elastic Beanstalk:** Assign an [IAM role to an instance](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/iam-instanceprofile.html).
-
-#### Environment variables
-
-You can specify an AWS Access Key ID and a Secret Access Key by setting the standard `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables.
-
-``` bash
-AWS_ACCESS_KEY_ID=my_access_key AWS_SECRET_ACCESS_KEY=my_secret_key imgproxy
-
-# same for Docker
-docker run -e AWS_ACCESS_KEY_ID=my_access_key -e AWS_SECRET_ACCESS_KEY=my_secret_key -it darthsim/imgproxy
-```
-
-#### Shared credentials file
-
-Alternatively, you can create the `.aws/credentials` file in your home directory with the following content:
-
-```ini
-[default]
-aws_access_key_id = %access_key_id
-aws_secret_access_key = %secret_access_key
-```
diff --git a/docs/configuration.md b/docs/configuration.md
deleted file mode 100644
index b7d1a3dafe..0000000000
--- a/docs/configuration.md
+++ /dev/null
@@ -1,556 +0,0 @@
-# Configuration
-
-imgproxy is [Twelve-Factor-App](https://12factor.net/)-ready and can be configured using `ENV` variables.
-
-## URL signature
-
-imgproxy allows URLs to be signed with a key and a salt. This feature is disabled by default, but is _highly_ recommended to be enabled in production. To enable URL signature checking, define the key/salt pair:
-
-* `IMGPROXY_KEY`: hex-encoded key
-* `IMGPROXY_SALT`: hex-encoded salt
-* `IMGPROXY_SIGNATURE_SIZE`: number of bytes to use for signature before encoding to Base64. Default: 32
-
-You can specify multiple key/salt pairs by dividing the keys and salts with a comma (`,`). imgproxy will check URL signatures with each pair. This is useful when you need to change key/salt pairs in your application while incurring zero downtime.
-
-You can also specify file paths using the command line by referencing a separate file containing hex-coded keys and salts line by line:
-
-```bash
-imgproxy -keypath /path/to/file/with/key -saltpath /path/to/file/with/salt
-```
-
-If you need a random key/salt pair really fast, as an example, you can quickly generate one using the following snippet:
-
-```bash
-echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
-```
-
-## Server
-
-* `IMGPROXY_BIND`: the address and port or Unix socket to listen to. Default: `:8080`
-* `IMGPROXY_NETWORK`: the network to use. Known networks are `tcp`, `tcp4`, `tcp6`, `unix`, and `unixpacket`. Default: `tcp`
-* `IMGPROXY_READ_TIMEOUT`: the maximum duration (in seconds) for reading the entire image request, including the body. Default: `10`
-* `IMGPROXY_WRITE_TIMEOUT`: the maximum duration (in seconds) for writing the response. Default: `10`
-* `IMGPROXY_KEEP_ALIVE_TIMEOUT`: the maximum duration (in seconds) to wait for the next request before closing the connection. When set to `0`, keep-alive is disabled. Default: `10`
-* `IMGPROXY_CLIENT_KEEP_ALIVE_TIMEOUT`: the maximum duration (in seconds) to wait for the next request before closing the HTTP client connection. The HTTP client is used to download source images. When set to `0`, keep-alive is disabled. Default: `90`
-* `IMGPROXY_DOWNLOAD_TIMEOUT`: the maximum duration (in seconds) for downloading the source image. Default: `5`
-* `IMGPROXY_WORKERS`: _(alias: `IMGPROXY_CONCURRENCY`)_ the maximum number of images an imgproxy instance can process simultaneously without creating a queue. Default: the number of CPU cores multiplied by two
-* `IMGPROXY_REQUESTS_QUEUE_SIZE`: the maximum number of image requests that can be put in the queue. Requests that exceed this limit are rejected with `429` HTTP status. When set to `0`, the requests queue is unlimited. Default: `0`
-* `IMGPROXY_MAX_CLIENTS`: the maximum number of simultaneous active connections. When set to `0`, connection limit is disabled. Default: `2048`
-* `IMGPROXY_TTL`: a duration (in seconds) sent via the `Expires` and `Cache-Control: max-age` HTTP headers. Default: `31536000` (1 year)
-* `IMGPROXY_CACHE_CONTROL_PASSTHROUGH`: when `true` and the source image response contains the `Expires` or `Cache-Control` headers, reuse those headers. Default: false
-* `IMGPROXY_SET_CANONICAL_HEADER`: when `true` and the source image has an `http` or `https` scheme, set a `rel="canonical"` HTTP header to the value of the source image URL. More details [here](https://developers.google.com/search/docs/advanced/crawling/consolidate-duplicate-urls#rel-canonical-header-method). Default: `false`
-* `IMGPROXY_SO_REUSEPORT`: when `true`, enables `SO_REUSEPORT` socket option (currently only available on Linux and macOS);
-* `IMGPROXY_PATH_PREFIX`: the URL path prefix. Example: when set to `/abc/def`, the imgproxy URL will be `/abc/def/%signature/%processing_options/%source_url`. Default: blank
-* `IMGPROXY_USER_AGENT`: the User-Agent header that will be sent with the source image request. Default: `imgproxy/%current_version`
-* `IMGPROXY_USE_ETAG`: when set to `true`, enables using the [ETag](https://en.wikipedia.org/wiki/HTTP_ETag) HTTP header for HTTP cache control. Default: `false`
-* `IMGPROXY_ETAG_BUSTER`: change this to change ETags for all the images. Default: blank
-* `IMGPROXY_USE_LAST_MODIFIED`: when set to `true`, enables using the [Last-Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) HTTP header for HTTP cache control. Default: `false`
-* `IMGPROXY_CUSTOM_REQUEST_HEADERS`: ![pro](./assets/pro.svg) list of custom headers that imgproxy will send while requesting the source image, divided by `\;` (can be redefined by `IMGPROXY_CUSTOM_HEADERS_SEPARATOR`). Example: `X-MyHeader1=Lorem\;X-MyHeader2=Ipsum`
-* `IMGPROXY_CUSTOM_RESPONSE_HEADERS`: ![pro](./assets/pro.svg) a list of custom response headers, separated by `\;` (can be redefined by `IMGPROXY_CUSTOM_HEADERS_SEPARATOR`). Example: `X-MyHeader1=Lorem\;X-MyHeader2=Ipsum`
-* `IMGPROXY_CUSTOM_HEADERS_SEPARATOR`: ![pro](./assets/pro.svg) a string that will be used as a custom header separator. Default: `\;`
-* `IMGPROXY_REQUEST_HEADERS_PASSTHROUGH`: ![pro](./assets/pro.svg) a list of names of incoming request headers that should be passed through to the source image request.
-* `IMGPROXY_RESPONSE_HEADERS_PASSTHROUGH`: ![pro](./assets/pro.svg) a list of names of source image response headers that should be passed through to the imgproxy response.
-* `IMGPROXY_ENABLE_DEBUG_HEADERS`: when set to `true`, imgproxy will add debug headers to the response. Default: `false`. The following headers will be added:
- * `X-Origin-Content-Length`: the size of the source image
- * `X-Origin-Width`: the width of the source image
- * `X-Origin-Height`: the height of the source image
- * `X-Result-Width`: the width of the resultant image
- * `X-Result-Height`: the height of the resultant image
-* `IMGPROXY_SERVER_NAME`: ![pro](./assets/pro.svg) the `Server` header value. Default: `imgproxy`
-
-## Security
-
-imgproxy protects you from so-called image bombs. Here's how you can specify the maximum image resolution which you consider reasonable:
-
-* `IMGPROXY_MAX_SRC_RESOLUTION`: the maximum resolution of the source image, in megapixels. Images with larger actual size will be rejected. Default: `16.8`
-
- **⚠️ Warning:** When the source image is animated, imgproxy summarizes all its frames' resolutions while checking the source image resolution unless `IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION` is greater than zero.
-
-* `IMGPROXY_MAX_SRC_FILE_SIZE`: the maximum size of the source image, in bytes. Images with larger file size will be rejected. When set to `0`, file size check is disabled. Default: `0`
-
-imgproxy can process animated images (GIF, WebP), but since this operation is pretty memory heavy, only one frame is processed by default. You can increase the maximum animation frames that can be processed number of with the following variable:
-
-* `IMGPROXY_MAX_ANIMATION_FRAMES`: the maximum number of animated image frames that may be processed. Default: `1`
-* `IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION`: the maximum resolution of the animated source image frame, in megapixels. Images with larger actual frame size will be rejected. When set to `0`, imgproxy will test the whole animated image resolution against `IMGPROXY_MAX_SRC_RESOLUTION` summarising all the frames' resolutions. Default: `0`
-
-To check if the source image is SVG, imgproxy reads some amount of bytes; by default it reads a maximum of 32KB. However, you can change this value using the following variable:
-
-* `IMGPROXY_MAX_SVG_CHECK_BYTES`: the maximum number of bytes imgproxy will read to recognize SVG files. If imgproxy is unable to recognize your SVG, try increasing this number. Default: `32768` (32KB)
-
-Requests to some image sources may go through too many redirects or enter an infinite loop. You can limit the number of allowed redirects:
-
-* `IMGPROXY_MAX_REDIRECTS`: the max number of redirects imgproxy can follow while requesting the source image. When set to `0`, no redirects are allowed. Default: `10`
-
-You can also specify a secret key to enable authorization with the HTTP `Authorization` header for use in production environments:
-
-* `IMGPROXY_SECRET`: the authorization token. If specified, the HTTP request should contain the `Authorization: Bearer %secret%` header.
-
-If you don't want to reveal your source URLs, you can encrypt them with the AES-CBC algorithm:
-
-* `IMGPROXY_SOURCE_URL_ENCRYPTION_KEY`: hex-encoded key used for source URL encryption. Default: blank
-
- **📝 Note:** Read more about source URL encryption in the [encrypting the source URL guide](encrypting_the_source_url.md).
-
-imgproxy does not send CORS headers by default. CORS will need to be allowed by using the following variable:
-
-* `IMGPROXY_ALLOW_ORIGIN`: when specified, enables CORS headers with the provided origin. CORS headers are disabled by default.
-
-You can limit allowed source URLs with the following variable:
-
-* `IMGPROXY_ALLOWED_SOURCES`: a whitelist of source image URL prefixes divided by comma. Wildcards can be included with `*` to match all characters except `/`. When blank, imgproxy allows all source image URLs. Example: `s3://,https://*.example.com/,local://`. Default: blank
-
- **⚠️ Warning:** Be careful when using this config to limit source URL hosts, and always add a trailing slash after the host.
-
- ❌ Bad: `http://example.com`
-
- ✅ Good: `http://example.com/`
-
- If the trailing slash is absent, `http://example.com@baddomain.com` would be a permissable URL, however, the request would be made to `baddomain.com`.
-
-* `IMGPROXY_ALLOW_LOOPBACK_SOURCE_ADDRESSES`: when `true`, allows connecting to loopback IP addresses (`127.0.0.1`-`127.255.255.255` and IPv6 analogues) when requesting source images. Default: `false`
-* `IMGPROXY_ALLOW_LINK_LOCAL_SOURCE_ADDRESSES`: when `true`, allows connecting to link-local multicast and unicast IP addresses (`224.0.0.1`-`224.0.0.255`, `169.254.0.1`-`169.254.255.255`, and IPv6 analogues) when requesting source images. Default: `false`
-* `IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES`: when `true`, allows connecting to private IP addresses (`10.0.0.0 - 10.255.255.255`, `172.16.0.0 - 172.31.255.255`, `192.168.0.0 - 192.168.255.255`, and IPv6 analogues) when requesting source images. Default: `true`
-
-* `IMGPROXY_SANITIZE_SVG`: when `true`, imgproxy will remove scripts from SVG images to prevent XSS attacks. Defaut: `true`
-
-When using imgproxy in a development environment, it can be useful to ignore SSL verification:
-
-* `IMGPROXY_IGNORE_SSL_VERIFICATION`: when `true`, disables SSL verification, so imgproxy can be used in a development environment with self-signed SSL certificates.
-
-Also you may want imgproxy to respond with the same error message that it writes to the log:
-
-* `IMGPROXY_DEVELOPMENT_ERRORS_MODE`: when `true`, imgproxy will respond with detailed error messages. Not recommended for production because some errors may contain stack traces.
-
-* `IMGPROXY_ALLOW_SECURITY_OPTIONS`: when `true`, allows usage of security-related processing options such as `max_src_resolution`, `max_src_file_size`, `max_animation_frames`, and `max_animation_frame_resolution`. Default: `false`.
-
- **⚠️ Warning:** `IMGPROXY_ALLOW_SECURITY_OPTIONS` allows bypassing your security restrictions. Don't set it to `true` unless you are completely sure that an attacker can't change your imgproxy URLs.
-
-## Cookies
-
-imgproxy can pass cookies in image requests. This can be activated with `IMGPROXY_COOKIE_PASSTHROUGH`. Unfortunately the `Cookie` header doesn't contain information about which URLs these cookies are applicable to, so imgproxy can only assume (or must be told).
-
-When cookie forwarding is activated, by default, imgproxy assumes the scope of the cookies to be all URLs with the same hostname/port and request scheme as given by the headers `X-Forwarded-Host`, `X-Forwarded-Port`, `X-Forwarded-Scheme` or `Host`. To change that use `IMGPROXY_COOKIE_BASE_URL`.
-
-* `IMGPROXY_COOKIE_PASSTHROUGH`: when `true`, incoming cookies will be passed through the image request if they are applicable for the image URL. Default: `false`
-
-* `IMGPROXY_COOKIE_BASE_URL`: when set, assume that cookies have the scope of this URL for an incoming request (instead of using request headers). If the cookies are applicable to the image URL too, they will be passed along in the image request.
-
-
-## Compression
-
-* `IMGPROXY_QUALITY`: the default quality of the resultant image, percentage. Default: `80`
-* `IMGPROXY_FORMAT_QUALITY`: default quality of the resulting image per format, separated by commas. Example: `jpeg=70,avif=40,webp=60`. When a value for the resulting format is not set, the `IMGPROXY_QUALITY` value is used. Default: `avif=65`
-
-### Advanced JPEG compression
-
-* `IMGPROXY_JPEG_PROGRESSIVE`: when `true`, enables progressive JPEG compression. Default: `false`
-* `IMGPROXY_JPEG_NO_SUBSAMPLE`: ![pro](./assets/pro.svg) when `true`, chrominance subsampling is disabled. This will improve quality at the cost of larger file size. Default: `false`
-* `IMGPROXY_JPEG_TRELLIS_QUANT`: ![pro](./assets/pro.svg) when `true`, enables trellis quantisation for each 8x8 block. Reduces file size but increases compression time. Default: `false`
-* `IMGPROXY_JPEG_OVERSHOOT_DERINGING`: ![pro](./assets/pro.svg) when `true`, enables overshooting of samples with extreme values. Overshooting may reduce ringing artifacts from compression, in particular in areas where black text appears on a white background. Default: `false`
-* `IMGPROXY_JPEG_OPTIMIZE_SCANS`: ![pro](./assets/pro.svg) when `true`, splits the spectrum of DCT coefficients into separate scans. Reduces file size but increases compression time. Requires `IMGPROXY_JPEG_PROGRESSIVE` to be true. Default: `false`
-* `IMGPROXY_JPEG_QUANT_TABLE`: ![pro](./assets/pro.svg) quantization table to use. Supported values are:
- * `0`: Table from JPEG Annex K (default)
- * `1`: Flat table
- * `2`: Table tuned for MSSIM on Kodak image set
- * `3`: Table from ImageMagick by N. Robidoux
- * `4`: Table tuned for PSNR-HVS-M on Kodak image set
- * `5`: Table from Relevance of Human Vision to JPEG-DCT Compression (1992)
- * `6`: Table from DCTune Perceptual Optimization of Compressed Dental X-Rays (1997)
- * `7`: Table from A Visual Detection Model for DCT Coefficient Quantization (1993)
- * `8`: Table from An Improved Detection Model for DCT Coefficient Quantization (1993)
-
-### Advanced PNG compression
-
-* `IMGPROXY_PNG_INTERLACED`: when `true`, enables interlaced PNG compression. Default: `false`
-* `IMGPROXY_PNG_QUANTIZE`: when `true`, enables PNG quantization. libvips should be built with [Quantizr](https://github.com/DarthSim/quantizr) or libimagequant support. Default: `false`
-* `IMGPROXY_PNG_QUANTIZATION_COLORS`: maximum number of quantization palette entries. Should be between 2 and 256. Default: 256
-
-
-
-### Advanced WebP compression
-
-* `IMGPROXY_WEBP_COMPRESSION`: ![pro](./assets/pro.svg) the compression method to use. Supported values are `lossy`, `near_lossless`, and `lossless`. Default: `lossy`
-
-### Advanced AVIF compression
-
-* `IMGPROXY_AVIF_SPEED`: controls the CPU effort spent improving compression. The lowest speed is at 0 and the fastest is at 9. Default: `9`
-
-### Autoquality
-
-imgproxy can calculate the quality of the resulting image based on selected metric. Read more in the [Autoquality](autoquality.md) guide.
-
-**⚠️ Warning:** Autoquality requires the image to be saved several times. Use it only when you prefer the resulting size and quality over the speed.
-
-* `IMGPROXY_AUTOQUALITY_METHOD`: ![pro](./assets/pro.svg) the method of quality calculation. Default: `none`
-* `IMGPROXY_AUTOQUALITY_TARGET`: ![pro](./assets/pro.svg) desired value of the autoquality method metric. Default: 0.02
-* `IMGPROXY_AUTOQUALITY_MIN`: ![pro](./assets/pro.svg) minimal quality imgproxy can use. Default: 70
-* `IMGPROXY_AUTOQUALITY_FORMAT_MIN`: ![pro](./assets/pro.svg) the minimal quality imgproxy can use per format, comma divided. Example: `jpeg=70,avif=40,webp=60`. When value for the resulting format is not set, `IMGPROXY_AUTOQUALITY_MIN` value is used. Default: `avif=40`
-* `IMGPROXY_AUTOQUALITY_MAX`: ![pro](./assets/pro.svg) the maximum quality imgproxy can use. Default: 80
-* `IMGPROXY_AUTOQUALITY_FORMAT_MAX`: ![pro](./assets/pro.svg) the maximum quality imgproxy can use per format, comma divided. Example: `jpeg=70,avif=40,webp=60`. When a value for the resulting format is not set, the `IMGPROXY_AUTOQUALITY_MAX` value is used. Default: `avif=50`
-* `IMGPROXY_AUTOQUALITY_ALLOWED_ERROR`: ![pro](./assets/pro.svg) the allowed `IMGPROXY_AUTOQUALITY_TARGET` error. Applicable only to `dssim` and `ml` methods. Default: 0.001
-* `IMGPROXY_AUTOQUALITY_MAX_RESOLUTION`: ![pro](./assets/pro.svg) when this value is greater then zero and the resultant resolution exceeds the value, autoquality won't be used. Default: 0
-* `IMGPROXY_AUTOQUALITY_JPEG_NET`: ![pro](./assets/pro.svg) the path to the neural network for JPEG.
-* `IMGPROXY_AUTOQUALITY_WEBP_NET`: ![pro](./assets/pro.svg) the path to the neural network for WebP.
-* `IMGPROXY_AUTOQUALITY_AVIF_NET`: ![pro](./assets/pro.svg) the path to the neural network for AVIF.
-
-## AVIF/WebP support detection
-
-imgproxy can use the `Accept` HTTP header to detect if the browser supports AVIF or WebP and use it as the default format. This feature is disabled by default and can be enabled by the following options:
-
-* `IMGPROXY_ENABLE_WEBP_DETECTION`: enables WebP support detection. When the file extension is omitted in the imgproxy URL and browser supports WebP, imgproxy will use it as the resulting format.
-* `IMGPROXY_ENFORCE_WEBP`: enables WebP support detection and enforces WebP usage. If the browser supports WebP, it will be used as resulting format even if another extension is specified in the imgproxy URL.
-* `IMGPROXY_ENABLE_AVIF_DETECTION`: enables AVIF support detection. When the file extension is omitted in the imgproxy URL and browser supports AVIF, imgproxy will use it as the resulting format.
-* `IMGPROXY_ENFORCE_AVIF`: enables AVIF support detection and enforces AVIF usage. If the browser supports AVIF, it will be used as resulting format even if another extension is specified in the imgproxy URL.
-
-**📝 Note:** imgproxy prefers AVIF over WebP. This means that if both AVIF and WebP detection/enforcement are enabled and the browser supports both of them, AVIF will be used.
-
-**📝 Note:** If both the source and the requested image formats support animation and AVIF detection/enforcement is enabled, AVIF won't be used as AVIF sequence is not supported yet.
-
-**📝 Note:** When AVIF/WebP support detection is enabled, please take care to configure your CDN or caching proxy to take the `Accept` HTTP header into account while caching.
-
-**⚠️ Warning:** Headers cannot be signed. This means that an attacker can bypass your CDN cache by changing the `Accept` HTTP headers. Keep this in mind when configuring your production caching setup.
-
-## Preferred formats
-
-When the resulting image format is not explicitly specified in the imgproxy URL via the extension or the `format` processing option, imgproxy will choose one of the preferred formats:
-
-* `IMGPROXY_PREFERRED_FORMATS`: a list of preferred formats, comma divided. Default: `jpeg,png,gif`
-
-imgproxy is guided by the following rules when choosing the resulting format:
-
-1. If the preferred formats list contains the source image format, it will be used
-2. If the resulting image is animated, the resulting image format should support animations
-3. If the resulting image contains transparency, the resulting image format should support transparency
-4. imgproxy chooses the first preferred format that meets those requirements
-5. If none of the preferred formats meet the requirements, the first preferred format is used
-
-**📝 Note:** When AVIF/WebP support detection is enabled and the browser supports AVIF/WebP, it may be used as the resultant format even if the preferred formats list doesn't contain it.
-
-## Skip processing
-
-You can configure imgproxy to skip processing of some formats:
-
-* `IMGPROXY_SKIP_PROCESSING_FORMATS`: a list of formats that imgproxy shouldn't process, comma divided.
-
-**📝 Note:** Processing can only be skipped when the requested format is the same as the source format.
-
-**📝 Note:** Video thumbnail processing can't be skipped.
-
-## Best format
-
-You can use the `best` value for the [format](generating_the_url.md#format) option or the [extension](generating_the_url.md#extension) to make imgproxy pick the best format for the resultant image.
-
-* `IMGPROXY_BEST_FORMAT_COMPLEXITY_THRESHOLD `: ![pro](./assets/pro.svg) the image complexity threshold. imgproxy will use a lossless or near-lossless encoding for images with low complexity. Default: `5.5`
-* `IMGPROXY_BEST_FORMAT_MAX_RESOLUTION`: ![pro](./assets/pro.svg) when greater than `0` and the image's resolution (in megapixels) is larger than the provided value, imgproxy won't try all the applicable formats and will just pick one that seems the best for the image
-* `IMGPROXY_BEST_FORMAT_BY_DEFAULT`: ![pro](./assets/pro.svg) when `true` and the resulting image format is not specified explicitly, imgproxy will use the `best` format instead of the source image format
-* `IMGPROXY_BEST_FORMAT_ALLOW_SKIPS`: ![pro](./assets/pro.svg) when `true` and the `best` format is used, imgproxy will skip processing of SVG and formats [listed to skip processing](configuration.md#skip-processing)
-
-Check out the [Best format](best_format.md) guide to learn more.
-
-## Client Hints support
-
-imgproxy can use the `Width` and `DPR` HTTP headers to determine default width and DPR options using Client Hints. This feature is disabled by default and can be enabled by the following option:
-
-* `IMGPROXY_ENABLE_CLIENT_HINTS`: enables Client Hints support to determine default width and DPR options. Read more details [here](https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints) about Client Hints.
-
-**⚠️ Warning:** Headers cannot be signed. This means that an attacker can bypass your CDN cache by changing the `Width` or `DPR` HTTP headers. Keep this in mind when configuring your production caching setup.
-
-## Video thumbnails
-
-imgproxy Pro can extract specific video frames to create thumbnails. This feature is disabled by default, but can be enabled with `IMGPROXY_ENABLE_VIDEO_THUMBNAILS`.
-
-* `IMGPROXY_ENABLE_VIDEO_THUMBNAILS`: ![pro](./assets/pro.svg) when `true`, enables video thumbnail generation. Default: `false`
-* `IMGPROXY_VIDEO_THUMBNAIL_SECOND`: ![pro](./assets/pro.svg) the timestamp of the frame (in seconds) that will be used for a thumbnail. Default: `1`
-* `IMGPROXY_VIDEO_THUMBNAIL_KEYFRAMES`: ![pro](./assets/pro.svg) when `true`, imgproxy will use the latest keyframe before `IMGPROXY_VIDEO_THUMBNAIL_SECOND` for video thumbnail generation. This makes video thumbnail generation faster yet the used frame timestamp will not be exactly equal to the requested one. Default: `false`
-* `IMGPROXY_VIDEO_THUMBNAIL_PROBE_SIZE`: ![pro](./assets/pro.svg) the maximum amount of bytes used to determine the format. Lower values can decrease memory usage but can produce inaccurate data, or even lead to errors. Default: 5000000
-* `IMGPROXY_VIDEO_THUMBNAIL_MAX_ANALYZE_DURATION`: ![pro](./assets/pro.svg) the maximum number of milliseconds used to get the stream info. Lower values can decrease memory usage but can produce inaccurate data, or even lead to errors. When set to `0`, the heuristic is used. Default: `0`
-
-**⚠️ Warning:** Though using `IMGPROXY_VIDEO_THUMBNAIL_PROBE_SIZE` and `IMGPROXY_VIDEO_THUMBNAIL_MAX_ANALYZE_DURATION` can lower the memory footprint of video thumbnail generation, they should be used in production only when you know what you're doing.
-
-## Watermark
-
-* `IMGPROXY_WATERMARK_DATA`: Base64-encoded image data. You can easily calculate it with `base64 tmp/watermark.png | tr -d '\n'`.
-* `IMGPROXY_WATERMARK_PATH`: the path to the locally stored image
-* `IMGPROXY_WATERMARK_URL`: the watermark image URL
-* `IMGPROXY_WATERMARK_OPACITY`: the watermark's base opacity
-* `IMGPROXY_WATERMARKS_CACHE_SIZE`: ![pro](./assets/pro.svg) custom watermarks cache size. When set to `0`, the watermark cache is disabled. 256 watermarks are cached by default.
-
-Read more about watermarks in the [Watermark](watermark.md) guide.
-
-## Unsharp masking
-
-imgproxy Pro can apply unsharp masking to your images.
-
-* `IMGPROXY_UNSHARP_MASKING_MODE`: ![pro](./assets/pro.svg) controls when unsharp masking should be applied. The following modes are supported:
- * `auto`: _(default)_ apply unsharp masking only when an image is downscaled and the `sharpen` option has not been set.
- * `none`: unsharp masking is not applied.
- * `always`: always applies unsharp masking.
-* `IMGPROXY_UNSHARP_MASKING_WEIGHT`: ![pro](./assets/pro.svg) a floating-point number that defines how neighboring pixels will affect the current pixel. The greater the value, the sharper the image. This value should be greater than zero. Default: `1`
-* `IMGPROXY_UNSHARP_MASKING_DIVIDER`: ![pro](./assets/pro.svg) a floating-point number that defines unsharp masking strength. The lesser the value, the sharper the image. This value be greater than zero. Default: `24`
-
-## Smart crop
-
-* `IMGPROXY_SMART_CROP_ADVANCED`: ![pro](./assets/pro.svg) when `true`, enables usage of the advanced smart crop method. Advanced smart crop may take more time than regular one, yet it produces better results.
-* `IMGPROXY_SMART_CROP_FACE_DETECTION`: ![pro](./assets/pro.svg) when `true`, adds an additional fast face detection step to smart crop.
-
-## Object detection
-
-imgproxy can detect objects on the image and use them to perform smart cropping, to blur the detections, or to draw the detections.
-
-* `IMGPROXY_OBJECT_DETECTION_CONFIG`: ![pro](./assets/pro.svg) the path to the neural network config. Default: blank
-* `IMGPROXY_OBJECT_DETECTION_WEIGHTS`: ![pro](./assets/pro.svg) the path to the neural network weights. Default: blank
-* `IMGPROXY_OBJECT_DETECTION_CLASSES`: ![pro](./assets/pro.svg) the path to the text file with the classes names, one per line. Default: blank
-* `IMGPROXY_OBJECT_DETECTION_NET_SIZE`: ![pro](./assets/pro.svg) the size of the neural network input. The width and the heights of the inputs should be the same, so this config value should be a single number. Default: 416
-* `IMGPROXY_OBJECT_DETECTION_CONFIDENCE_THRESHOLD`: ![pro](./assets/pro.svg) detections with confidences below this value will be discarded. Default: 0.2
-* `IMGPROXY_OBJECT_DETECTION_NMS_THRESHOLD`: ![pro](./assets/pro.svg) non-max supression threshold. Don't change this if you don't know what you're doing. Default: 0.4
-
-## Fallback image
-
-You can set up a fallback image that will be used in case imgproxy is unable to fetch the requested one. Use one of the following variables:
-
-* `IMGPROXY_FALLBACK_IMAGE_DATA`: Base64-encoded image data. You can easily calculate it with `base64 tmp/fallback.png | tr -d '\n'`.
-* `IMGPROXY_FALLBACK_IMAGE_PATH`: the path to the locally stored image
-* `IMGPROXY_FALLBACK_IMAGE_URL`: the fallback image URL
-* `IMGPROXY_FALLBACK_IMAGE_HTTP_CODE`: the HTTP code for the fallback image response. When set to zero, imgproxy will respond with the usual HTTP code. Default: `200`
-* `IMGPROXY_FALLBACK_IMAGE_TTL`: a duration (in seconds) sent via the `Expires` and `Cache-Control: max-age` HTTP headers when a fallback image was used. When blank or `0`, the value from `IMGPROXY_TTL` is used.
-* `IMGPROXY_FALLBACK_IMAGES_CACHE_SIZE`: ![pro](./assets/pro.svg) the size of custom fallback images cache. When set to `0`, the fallback image cache is disabled. 256 fallback images are cached by default.
-
-## Presets
-
-Read more about imgproxy presets in the [Presets](presets.md) guide.
-
-There are two ways to define presets:
-
-#### Using an environment variable
-
-* `IMGPROXY_PRESETS`: a set of processing preset definitions, comma divided. Example: `default=resizing_type:fill/enlarge:1,sharp=sharpen:0.7,blurry=blur:2`. Default: blank
-* `IMGPROXY_INFO_PRESETS`: ![pro](./assets/pro.svg) a set of info preset definitions, comma divided. Example: `default=xmp:0/blurhash:4:3`. Default: blank
-
-#### Using a command line argument
-
-```bash
-imgproxy -presets /path/to/file/with/presets -info-presets /path/to/file/with/info-presets
-```
-
-This file should contain preset definitions, one per line. Lines starting with `#` are treated as comments. Example:
-
-```
-default=resizing_type:fill/enlarge:1
-
-# Sharpen the image to make it look better
-sharp=sharpen:0.7
-
-# Blur the image to hide details
-blurry=blur:2
-```
-
-### Using only presets
-
-imgproxy can be switched into "presets-only mode". In this mode, imgproxy accepts only `preset` option arguments as processing options. Example: `http://imgproxy.example.com/unsafe/thumbnail:blurry:watermarked/plain/http://example.com/images/curiosity.jpg@png`
-
-* `IMGPROXY_ONLY_PRESETS`: when `true`, enables presets-only mode. Default: `false`
-* `IMGPROXY_INFO_ONLY_PRESETS`: when `true`, enables presets-only mode for the [info](getting_the_image_info.md) endpoint. Default: `IMGPROXY_ONLY_PRESETS` value
-
-## Image sources
-
-### Local files :id=serving-local-files
-
-imgproxy can serve your local images, but this feature is disabled by default. To enable it, specify your local filesystem root:
-
-* `IMGPROXY_LOCAL_FILESYSTEM_ROOT`: the root of the local filesystem. Keep this empty to disable local file serving.
-
-Check out the [Serving local files](serving_local_files.md) guide to learn more.
-
-### Amazon S3 :id=serving-files-from-amazon-s3
-
-imgproxy can process files from Amazon S3 buckets, but this feature is disabled by default. To enable it, set `IMGPROXY_USE_S3` to `true`:
-
-* `IMGPROXY_USE_S3`: when `true`, enables image fetching from Amazon S3 buckets. Default: `false`
-* `IMGPROXY_S3_REGION`: an S3 buckets region
-* `IMGPROXY_S3_ENDPOINT`: a custom S3 endpoint to being used by imgproxy
-* `IMGPROXY_S3_MULTI_REGION`: when `true`, allows using S3 buckets from different regions
-
-Check out the [Serving files from S3](serving_files_from_s3.md) guide to learn more.
-
-### Google Cloud Storage :id=serving-files-from-google-cloud-storage
-
-imgproxy can process files from Google Cloud Storage buckets, but this feature is disabled by default. To enable it, set the value of `IMGPROXY_USE_GCS` to `true`:
-
-* `IMGPROXY_USE_GCS`: when `true`, enables image fetching from Google Cloud Storage buckets. Default: `false`
-* `IMGPROXY_GCS_KEY`: the Google Cloud JSON key. When set, enables image fetching from Google Cloud Storage buckets. Default: blank
-* `IMGPROXY_GCS_ENDPOINT`: a custom Google Cloud Storage endpoint to being used by imgproxy
-
-Check out the [Serving files from Google Cloud Storage](serving_files_from_google_cloud_storage.md) guide to learn more.
-
-### Azure Blob Storage :id=serving-files-from-azure-blob-storage
-
-imgproxy can process files from Azure Blob Storage containers, but this feature is disabled by default. To enable it, set `IMGPROXY_USE_ABS` to `true`:
-
-* `IMGPROXY_USE_ABS`: when `true`, enables image fetching from Azure Blob Storage containers. Default: `false`
-* `IMGPROXY_ABS_NAME`: the Azure account name. Default: blank
-* `IMGPROXY_ABS_KEY`: the Azure account key. Default: blank
-* `IMGPROXY_ABS_ENDPOINT`: the custom Azure Blob Storage endpoint to be used by imgproxy. Default: blank
-
-Check out the [Serving files from Azure Blob Storage](serving_files_from_azure_blob_storage.md) guide to learn more.
-
-### OpenStack Object Storage ("Swift") :id=serving-files-from-openstack-object-storage-swift
-
-imgproxy can process files from OpenStack Object Storage, but this feature is disabled by default. To enable it, set `IMGPROXY_USE_SWIFT` to `true`.
-* `IMGPROXY_USE_SWIFT`: when `true`, enables image fetching from OpenStack Swift Object Storage. Default: `false`
-* `IMGPROXY_SWIFT_USERNAME`: the username for Swift API access. Default: blank
-* `IMGPROXY_SWIFT_API_KEY`: the API key for Swift API access. Default: blank
-* `IMGPROXY_SWIFT_AUTH_URL`: the Swift Auth URL. Default: blank
-* `IMGPROXY_SWIFT_AUTH_VERSION`: the Swift auth version, set to 1, 2 or 3 or leave at 0 for autodetect.
-* `IMGPROXY_SWIFT_TENANT`: the tenant name (optional, v2 auth only). Default: blank
-* `IMGPROXY_SWIFT_DOMAIN`: the Swift domain name (optional, v3 auth only): Default: blank
-* `IMGRPOXY_SWIFT_TIMEOUT_SECONDS`: the data channel timeout in seconds. Default: 60
-* `IMGRPOXY_SWIFT_CONNECT_TIMEOUT_SECONDS`: the connect channel timeout in seconds. Default: 10
-
-Check out the [Serving files from OpenStack Object Storage](serving_files_from_openstack_swift.md) guide to learn more.
-
-## Source image URLs
-
-* `IMGPROXY_BASE_URL`: a base URL prefix that will be added to each source image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. If the image URL already contains the prefix, it won't be added. Default: blank
-
-* `IMGPROXY_URL_REPLACEMENTS`: a list of `pattern=replacement` pairs, semicolon (`;`) divided. imgproxy will replace source URL prefixes matching the pattern with the corresponding replacement. Wildcards can be included in patterns with `*` to match all characters except `/`. `${N}` in replacement strings will be replaced with wildcard values, where `N` is the number of the wildcard. Examples:
- * `mys3://=s3://my_bucket/images/` will replace `mys3://image01.jpg` with `s3://my_bucket/images/image01.jpg`
- * `mys3://*/=s3://my_bucket/${1}/images` will replace `mys3://items/image01.jpg` with `s3://my_bucket/items/images/image01.jpg`
-
-**📝 Note:** Replacements defined in `IMGPROXY_URL_REPLACEMENTS` are applied before `IMGPROXY_BASE_URL` is added.
-
-## Metrics
-
-### New Relic :id=new-relic-metrics
-
-imgproxy can send its metrics to New Relic. Specify your New Relic license key to activate this feature:
-
-* `IMGPROXY_NEW_RELIC_KEY`: the New Relic license key
-* `IMGPROXY_NEW_RELIC_APP_NAME`: a New Relic application name. Default: `imgproxy`
-* `IMGPROXY_NEW_RELIC_LABELS`: the list of New Relic labels, semicolon divided. Example: `label1=value1;label2=value2`. Default: blank
-
-Check out the [New Relic](new_relic.md) guide to learn more.
-
-### Prometheus :id=prometheus-metrics
-
-imgproxy can collect its metrics for Prometheus. Specify a binding for Prometheus metrics server to activate this feature:
-
-* `IMGPROXY_PROMETHEUS_BIND`: Prometheus metrics server binding. Can't be the same as `IMGPROXY_BIND`. Default: blank
-* `IMGPROXY_PROMETHEUS_NAMESPACE`: Namespace (prefix) for imgproxy metrics. Default: blank
-
-Check out the [Prometheus](prometheus.md) guide to learn more.
-
-### Datadog :id=datadog-metrics
-
-imgproxy can send its metrics to Datadog:
-
-* `IMGPROXY_DATADOG_ENABLE`: when `true`, enables sending metrics to Datadog. Default: false
-* `IMGPROXY_DATADOG_ENABLE_ADDITIONAL_METRICS`: when `true`, enables sending the additional metrics to Datadog. Default: false
-
-**⚠️ Warning:** Since the additional metrics are treated by Datadog as custom, Datadog can additionally bill you for their usage. Please, check out Datadog's [Custom Metrics Billing](https://docs.datadoghq.com/account_management/billing/custom_metrics/) page for additional details.
-
-Check out the [Datadog](datadog.md) guide to learn more.
-
-### OpenTelemetry :id=opentelemetry-metrics
-
-imgproxy can send request traces to an OpenTelemetry collector:
-
-* `IMGPROXY_OPEN_TELEMETRY_ENDPOINT`: OpenTelemetry collector endpoint (`host:port`). Default: blank
-* `IMGPROXY_OPEN_TELEMETRY_PROTOCOL`: OpenTelemetry collector protocol. Supported protocols are `grpc`, `https`, and `http`. Default: `grpc`
-* `IMGPROXY_OPEN_TELEMETRY_SERVICE_NAME`: OpenTelemetry service name. Default: `imgproxy`
-* `IMGPROXY_OPEN_TELEMETRY_ENABLE_METRICS`: when `true`, imgproxy will send metrics over OpenTelemetry Metrics API. Default: `false`
-* `IMGPROXY_OPEN_TELEMETRY_SERVER_CERT`: OpenTelemetry collector TLS certificate, PEM-encoded (you can replace line breaks with `\n`). Default: blank
-* `IMGPROXY_OPEN_TELEMETRY_CLIENT_CERT`: OpenTelemetry client TLS certificate, PEM-encoded (you can replace line breaks with `\n`). Default: blank
-* `IMGPROXY_OPEN_TELEMETRY_CLIENT_KEY`: OpenTelemetry client TLS key, PEM-encoded (you can replace line breaks with `\n`). Default: blank
-* `IMGPROXY_OPEN_TELEMETRY_GRPC_INSECURE`: when `true`, imgproxy will use an insecure GRPC connection unless the collector TLS certificate is not provided. Default: `true`
-* `IMGPROXY_OPEN_TELEMETRY_PROPAGATORS`: a list of OpenTelemetry text map propagators, comma divided. Supported propagators are `tracecontext`, `baggage`, `b3`, `b3multi`, `jaeger`, `xray`, and `ottrace`. Default: blank
-* `IMGPROXY_OPEN_TELEMETRY_TRACE_ID_GENERATOR`: OpenTelemetry trace ID generator. Supported generators are `xray` and `random`. Default: `xray`
-* `IMGPROXY_OPEN_TELEMETRY_CONNECTION_TIMEOUT`: the maximum duration (in seconds) for establishing a connection to the OpenTelemetry collector. Default: `5`
-
-Check out the [OpenTelemetry](open_telemetry.md) guide to learn more.
-
-### Amazon CloudWatch metrics :id=amazon-cloudwatch-metrics
-
-imgproxy can send its metrics to Amazon CloudWatch. Specify a desired `ServiceName` dimesion value to activate this feature:
-
-* `IMGPROXY_CLOUD_WATCH_SERVICE_NAME`: the value of the `ServiceName` dimension which will be used in the metrics. Default: blank
-* `IMGPROXY_CLOUD_WATCH_NAMESPACE`: the CloudWatch namespace for the metrics
-* `IMGPROXY_CLOUD_WATCH_REGION`: the code of the AWS region to which the metrics should be sent
-
-Check out the [CloudWatch](cloud_watch.md) guide to learn more.
-
-## Error reporting
-
-imgproxy can report occurred errors to Bugsnag, Honeybadger and Sentry:
-
-* `IMGPROXY_REPORT_DOWNLOADING_ERRORS`: when `true`, imgproxy will report downloading errors. Default: `true`
-
-### Bugsnag
-
-* `IMGPROXY_BUGSNAG_KEY`: Bugsnag API key. When provided, enables error reporting to Bugsnag.
-* `IMGPROXY_BUGSNAG_STAGE`: the Bugsnag stage to report to. Default: `production`
-
-### Honeybadger
-
-* `IMGPROXY_HONEYBADGER_KEY`: the Honeybadger API key. When provided, enables error reporting to Honeybadger.
-* `IMGPROXY_HONEYBADGER_ENV`: the Honeybadger env to report to. Default: `production`
-
-### Sentry
-
-* `IMGPROXY_SENTRY_DSN`: Sentry project DSN. When provided, enables error reporting to Sentry.
-* `IMGPROXY_SENTRY_ENVIRONMENT`: the Sentry environment to report to. Default: `production`
-* `IMGPROXY_SENTRY_RELEASE`: the Sentry release to report to. Default: `imgproxy@{imgproxy version}`
-
-### Airbrake
-
-* `IMGPROXY_AIRBRAKE_PROJECT_ID`: an Airbrake project id
-* `IMGPROXY_AIRBRAKE_PROJECT_KEY`: an Airbrake project key
-* `IMGPROXY_AIRBRAKE_ENVIRONMENT`: the Airbrake environment to report to. Default: `production`
-
-## Log
-
-* `IMGPROXY_LOG_FORMAT`: the log format. The following formats are supported:
- * `pretty`: _(default)_ colored human-readable format
- * `structured`: machine-readable format
- * `json`: JSON format
- * `gcp`: Google Cloud Logging agent compliant format
-* `IMGPROXY_LOG_LEVEL`: the log level. The following levels are supported `error`, `warn`, `info` and `debug`. Default: `info`
-
-imgproxy can send logs to syslog, but this feature is disabled by default. To enable it, set `IMGPROXY_SYSLOG_ENABLE` to `true`:
-
-* `IMGPROXY_SYSLOG_ENABLE`: when `true`, enables sending logs to syslog.
-* `IMGPROXY_SYSLOG_LEVEL`: the maximum log level to send to syslog. Known levels are: `crit`, `error`, `warning` and `info`. Default: `info`
-* `IMGPROXY_SYSLOG_NETWORK`: the network that will be used to connect to syslog. When blank, the local syslog server will be used. Known networks are `tcp`, `tcp4`, `tcp6`, `udp`, `udp4`, `udp6`, `ip`, `ip4`, `ip6`, `unix`, `unixgram` and `unixpacket`. Default: blank
-* `IMGPROXY_SYSLOG_ADDRESS`: the address of the syslog service. Not used if `IMGPROXY_SYSLOG_NETWORK` is blank. Default: blank
-* `IMGPROXY_SYSLOG_TAG`: the specific syslog tag. Default: `imgproxy`
-
-**📝 Note:** imgproxy always uses structured log format for syslog.
-
-## Memory usage tweaks
-
-**⚠️ Warning:** We highly recommended reading the [Memory usage tweaks](memory_usage_tweaks.md) guide before changing these settings.
-
-* `IMGPROXY_DOWNLOAD_BUFFER_SIZE`: the initial size (in bytes) of a single download buffer. When set to zero, initializes empty download buffers. Default: `0`
-* `IMGPROXY_FREE_MEMORY_INTERVAL`: the interval (in seconds) at which unused memory will be returned to the OS. Default: `10`
-* `IMGPROXY_BUFFER_POOL_CALIBRATION_THRESHOLD`: the number of buffers that should be returned to a pool before calibration. Default: `1024`
-* `IMGPROXY_MALLOC`: _(Docker only)_ malloc implementation to use. The following implementations are supported:
- * `malloc`: standard malloc implementation
- * `jemalloc`: https://jemalloc.net/
- * `tcmalloc`: https://github.com/google/tcmalloc
-
-## Miscellaneous
-
-* `IMGPROXY_USE_LINEAR_COLORSPACE`: when `true`, imgproxy will process images in linear colorspace. This will slow down processing. Note that images won't be fully processed in linear colorspace while shrink-on-load is enabled (see below).
-* `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEGs and WebP files. Allows processing the entire image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images.
-* `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true`
-* `IMGPROXY_KEEP_COPYRIGHT`: when `true`, imgproxy will not remove copyright info while stripping metadata. Default: `true`
-* `IMGPROXY_STRIP_METADATA_DPI`: ![pro](./assets/pro.svg) the DPI metadata value that should be set for the image when its metadata is stripped. Default: `72.0`
-* `IMGPROXY_STRIP_COLOR_PROFILE`: when `true`, imgproxy will transform the embedded color profile (ICC) to sRGB and remove it from the image. Otherwise, imgproxy will try to keep it as is. Default: `true`
-* `IMGPROXY_AUTO_ROTATE`: when `true`, imgproxy will automatically rotate images based on the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image in all cases. Default: `true`
-* `IMGPROXY_ENFORCE_THUMBNAIL`: when `true` and the source image has an embedded thumbnail, imgproxy will always use the embedded thumbnail instead of the main image. Currently, only thumbnails embedded in `heic` and `avif` are supported. Default: `false`
-* `IMGPROXY_RETURN_ATTACHMENT`: when `true`, response header `Content-Disposition` will include `attachment`. Default: `false`
-* `IMGPROXY_SVG_FIX_UNSUPPORTED`: when `true`, imgproxy will try to replace SVG features unsupported by librsvg to minimize SVG rendering error. This config only takes effect on SVG rasterization. Default: `false`
-* `IMGPROXY_HEALTH_CHECK_MESSAGE`: ![pro](./assets/pro.svg) the content of the health check response. Default: `imgproxy is running`
-* `IMGPROXY_HEALTH_CHECK_PATH`: an additional path of the health check. Default: blank
diff --git a/docs/datadog.md b/docs/datadog.md
deleted file mode 100644
index 9121c0dd1a..0000000000
--- a/docs/datadog.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Datadog
-
-imgproxy can send its metrics to Datadog. To use this feature, do the following:
-
-1. Install & configure the Datadog Trace Agent (>= 5.21.1).
-2. Set the `IMGPROXY_DATADOG_ENABLE` environment variable to `true`.
-3. Configure the Datadog tracer using `ENV` variables provided by [the package](https://github.com/DataDog/dd-trace-go):
-
- * `DD_AGENT_HOST`: sets the address to connect to for sending metrics to the Datadog Agent. Default: `localhost`
- * `DD_TRACE_AGENT_PORT`: sets the Datadog Agent Trace port. Default: `8126`
- * `DD_DOGSTATSD_PORT`: set the DogStatsD port. Default: `8125`
- * `DD_SERVICE`: sets the desired application name. Default: `imgproxy`
- * `DD_ENV`: specifies the environment to which all traces will be submitted. Default: empty
- * `DD_TRACE_SOURCE_HOSTNAME`: specifies the hostname with which to mark outgoing traces. Default: empty
- * `DD_TRACE_REPORT_HOSTNAME`: when `true`, sets hostname to `os.Hostname()` with which to mark outgoing traces. Default: `false`
- * `DD_TAGS`: sets a key/value pair which will be set as a tag on all traces. Example: `DD_TAGS=datacenter:njc,key2:value2`. Default: empty
- * `DD_TRACE_ANALYTICS_ENABLED`: allows specifying whether Trace Search & Analytics should be enabled for integrations. Default: `false`
- * `DD_RUNTIME_METRICS_ENABLED`: enables automatic collection of runtime metrics every 10 seconds. Default: `false`
- * `DD_TRACE_STARTUP_LOGS`: causes various startup info to be written when the tracer starts. Default: `true`
- * `DD_TRACE_DEBUG`: enables detailed logs. Default: `false`
-4. _(optional)_ Set the `IMGPROXY_DATADOG_ENABLE_ADDITIONAL_METRICS` environment variable to `true` to collect the [additional metrics](#additional-metrics).
-
-imgproxy will send the following info to Datadog:
-
-* Response time
-* Queue time
-* Image downloading time
-* Image processing time
-* Errors that occurred while downloading and processing image
-
-## Additional metrics
-
-When the `IMGPROXY_DATADOG_ENABLE_ADDITIONAL_METRICS` environment variable is set to `true`, imgproxy will send the following additional metrics to Datadog:
-
-* `imgproxy.requests_in_progress`: the number of requests currently in progress
-* `imgproxy.images_in_progress`: the number of images currently in progress
-* `imgproxy.buffer.size`: a histogram of the download buffers sizes (in bytes)
-* `imgproxy.buffer.default_size`: calibrated default buffer size (in bytes)
-* `imgproxy.buffer.max_size`: calibrated maximum buffer size (in bytes)
-* `imgproxy.vips.memory`: libvips memory usage (in bytes)
-* `imgproxy.vips.max_memory`: libvips maximum memory usage (in bytes)
-* `imgproxy.vips.allocs`: the number of active vips allocations
diff --git a/docs/encrypting_the_source_url.md b/docs/encrypting_the_source_url.md
deleted file mode 100644
index 002c0b90c0..0000000000
--- a/docs/encrypting_the_source_url.md
+++ /dev/null
@@ -1,98 +0,0 @@
-# Encrypting the source URL![pro](./assets/pro.svg)
-
-If you don't want to reveal your source URLs, you can encrypt them with the AES-CBC algorithm.
-
-### Configuring source URL encryption
-
-The only thing needed for source URL encryption is a key:
-
-* `IMGPROXY_SOURCE_URL_ENCRYPTION_KEY`: hex-encoded key used for source URL encryption. Default: blank
-
-The key should be either 16, 24, or 32 bytes long for AES-128-CBC, AES-192-CBC, or AES-256-CBC, respectively.
-
-If you need a random key in a hurry, you can quickly generate one using the following snippet:
-
-```bash
-echo $(xxd -g 2 -l 32 -p /dev/random | tr -d '\n')
-```
-
-### Encrypting the source URL
-
-* Pad your source URL using the [PKCS #7](https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7) method so it becomes 16-byte aligned. Some libraries like Ruby's `openssl` do the message padding for you
-* Generate a 16-byte long initialization vector (IV)
-* Encrypt the padded source URL with the AES-CBC algorithm using the configured key and the IV generated in the previous step
-* Create the following string: IV + encrypted URL
-* Encode the result of the previous step with URL-safe Base64
-
-#### IV generation
-
-AES-CBC requires IV to be unique between unencrypted messages (source URLs in our case). Usually, it's recommended to use a counter when generating an IV to be sure it never repeats. However, in our case, this leads to a major drawback: using a unique IV every time you encrypt the same source URL will lead to different cipher texts and thus different imgproxy URLs. And this leads to a situation where requests to imgproxy will never hit the CDNs cache.
-
-On the other hand, reusing the IV with the same message is safe but ONLY while with this message. Thus, there are some tradeoffs:
-
-1. Cache IVs. Store IV somewhere so you need to generate it only once for each source URL and extract it if needed. Depending on the level of security you need, you may also want to encrypt stored IVs with a different key so a DB leak won't reveal the message-IV pairs.
-2. Use a deterministic method of generation. For example, you can calculate an HMAC hash of the plain source URL with a different key and truncate it to the IV size. Though this method doesn't guarantee that it will always generate unique IVs, the chances of generating repeatable IVs with it are considerably rare.
-
-### Example
-
-**You can find helpful code snippets in various programming languages in the [examples](https://github.com/imgproxy/imgproxy/tree/master/examples) folder. There's a good chance you'll find a snippet in your favorite programming language that you'll be able to use right away.**
-
-And here is a step-by-step example of a source URL encryption:
-
-Before we start, we need an encryption key. We will use the AES-256-CBC algorithm in this example, so we need a 32-byte key. Let's assume we used a random generator and got the following hex-encoded key:
-
-```
-1eb5b0e971ad7f45324c1bb15c947cb207c43152fa5c6c7f35c4f36e0c18e0f1
-```
-
-Run imgproxy using this encryption key, like so:
-
-```bash
-IMGPROXY_SOURCE_URL_ENCRYPTION_KEY="1eb5b0e971ad7f45324c1bb15c947cb207c43152fa5c6c7f35c4f36e0c18e0f1" imgproxy
-```
-
-Next, assume that you have the following source URL:
-
-```
-http://example.com/images/curiosity.jpg
-```
-
-It's 39-byte long, so we should align it to 16 bytes using the PKCS #7 method:
-
-```
-http://example.com/images/curiosity.jpg\09\09\09\09\09\09\09\09\09
-```
-
-**📝 Note:** From this point on, we'll show unprintable characters in `\NN` format where `NN` is a hex representation of the byte.
-
-Next, we need an initialization vector (IV). Let's assume we generated the following IV:
-
-```
-\A7\95\63\A2\B3\5D\86\CE\E6\45\1C\3C\80\0F\53\5A
-```
-
-We'll use our encription key and IV encrypt a 16-byte-aligned source URL with the AES-256-CBC algorithm:
-
-```
-\84\65\19\C8\B7\97\59\2E\CE\A3\78\DD\44\25\45\A4\48\43\4A\AD\04\A5\B7\A8\50\01\22\CC\7E\65\1C\FF\71\57\3C\89\54\D8\6E\1B\0D\B3\13\41\2F\50\47\69
-```
-
-Add the IV to the beginning:
-
-```
-\A7\95\63\A2\B3\5D\86\CE\E6\45\1C\3C\80\0F\53\5A\84\65\19\C8\B7\97\59\2E\CE\A3\78\DD\44\25\45\A4\48\43\4A\AD\04\A5\B7\A8\50\01\22\CC\7E\65\1C\FF\71\57\3C\89\54\D8\6E\1B\0D\B3\13\41\2F\50\47\69
-```
-
-And finally, encode the result with URL-safe Base64:
-
-```
-p5VjorNdhs7mRRw8gA9TWoRlGci3l1kuzqN43UQlRaRIQ0qtBKW3qFABIsx-ZRz_cVc8iVTYbhsNsxNBL1BHaQ
-```
-
-Now you can put this encrypted URL in the imgproxy URL path, prepending it with the `/enc/` segment:
-
-```
-/unsafe/rs:fit:300:300/enc/p5VjorNdhs7mRRw8gA9TWoRlGci3l1kuzqN43UQlRaRIQ0qtBKW3qFABIsx-ZRz_cVc8iVTYbhsNsxNBL1BHaQ
-```
-
-**📝 Note:** The imgproxy URL in this example is not signed but signing URLs is especially important when using encrypted source URLs to prevent a padding oracle attack.
diff --git a/docs/generating_the_url.md b/docs/generating_the_url.md
deleted file mode 100644
index dc8dfac71d..0000000000
--- a/docs/generating_the_url.md
+++ /dev/null
@@ -1,978 +0,0 @@
-# Generating the URL
-
-The URL should contain the signature, processing options, and source URL, like this:
-
-```
-/%signature/%processing_options/plain/%source_url@%extension
-/%signature/%processing_options/%encoded_source_url.%extension
-```
-
-Check out the [example](#example) at the end of this guide.
-
-## Signature
-
-**⚠️ Warning:** The signature part should always be present in a URL. If the signature check is disabled (no key/salt pairs are provided), the signature part may contain anything (for example, `unsafe` or `_`).
-
-A signature protects your URL from being altered by an attacker. It is highly recommended to sign imgproxy URLs when imgproxy is being used in production.
-
-Once you set up your [URL signature](configuration.md#url-signature), check out the [Signing the URL](signing_the_url.md) guide to find out how to sign your URLs. Otherwise, since the signature still needs to be present, feel free to use any string here.
-
-## Processing options
-
-Processing options should be specified as URL parts divided by slashes (`/`). A processing option has the following format:
-
-```
-%option_name:%argument1:%argument2:...:argumentN
-```
-
-The list of processing options does not define imgproxy's processing pipeline. Instead, imgproxy already comes with a specific, built-in image processing pipeline for maximum performance. Read more about this in the [About processing pipeline](about_processing_pipeline.md) guide.
-
-imgproxy supports the following processing options:
-
-### Resize
-
-```
-resize:%resizing_type:%width:%height:%enlarge:%extend
-rs:%resizing_type:%width:%height:%enlarge:%extend
-```
-
-This is a meta-option that defines the [resizing type](#resizing-type), [width](#width), [height](#height), [enlarge](#enlarge), and [extend](#extend). All arguments are optional and can be omitted to use their default values.
-
-### Size
-
-```
-size:%width:%height:%enlarge:%extend
-s:%width:%height:%enlarge:%extend
-```
-
-This is a meta-option that defines the [width](#width), [height](#height), [enlarge](#enlarge), and [extend](#extend). All arguments are optional and can be omitted to use their default values.
-
-### Resizing type
-
-```
-resizing_type:%resizing_type
-rt:%resizing_type
-```
-
-Defines how imgproxy will resize the source image. Supported resizing types are:
-
-* `fit`: resizes the image while keeping aspect ratio to fit a given size.
-* `fill`: resizes the image while keeping aspect ratio to fill a given size and crops projecting parts.
-* `fill-down`: the same as `fill`, but if the resized image is smaller than the requested size, imgproxy will crop the result to keep the requested aspect ratio.
-* `force`: resizes the image without keeping the aspect ratio.
-* `auto`: if both source and resulting dimensions have the same orientation (portrait or landscape), imgproxy will use `fill`. Otherwise, it will use `fit`.
-
-Default: `fit`
-
-### Resizing algorithm![pro](./assets/pro.svg) :id=resizing-algorithm
-
-```
-resizing_algorithm:%algorithm
-ra:%algorithm
-```
-
-Defines the algorithm that imgproxy will use for resizing. Supported algorithms are `nearest`, `linear`, `cubic`, `lanczos2`, and `lanczos3`.
-
-Default: `lanczos3`
-
-### Width
-
-```
-width:%width
-w:%width
-```
-
-Defines the width of the resulting image. When set to `0`, imgproxy will calculate width using the defined height and source aspect ratio. When set to `0` and resizing type is `force`, imgproxy will keep the original width.
-
-Default: `0`
-
-### Height
-
-```
-height:%height
-h:%height
-```
-
-Defines the height of the resulting image. When set to `0`, imgproxy will calculate resulting height using the defined width and source aspect ratio. When set to `0` and resizing type is `force`, imgproxy will keep the original height.
-
-Default: `0`
-
-### Min width
-
-```
-min-width:%width
-mw:%width
-```
-
-Defines the minimum width of the resulting image.
-
-**⚠️ Warning:** When both `width` and `min-width` are set, the final image will be cropped according to `width`, so use this combination with care.
-
-Default: `0`
-
-### Min height
-
-```
-min-height:%height
-mh:%height
-```
-
-Defines the minimum height of the resulting image.
-
-**⚠️ Warning:** When both `height` and `min-height` are set, the final image will be cropped according to `height`, so use this combination with care.
-
-Default: `0`
-
-### Zoom
-
-```
-zoom:%zoom_x_y
-z:%zoom_x_y
-
-zoom:%zoom_x:%zoom_y
-z:%zoom_x:%zoom_y
-```
-
-When set, imgproxy will multiply the image dimensions according to these factors. The values must be greater than 0.
-
-Can be combined with `width` and `height` options. In this case, imgproxy calculates scale factors for the provided size and then multiplies it with the provided zoom factors.
-
-**📝 Note:** Unlike the `dpr` option, the `zoom` option doesn't affect gravities offsets, watermark offsets, and paddings.
-
-Default: `1`
-
-### Dpr
-
-```
-dpr:%dpr
-```
-
-When set, imgproxy will multiply the image dimensions according to this factor for HiDPI (Retina) devices. The value must be greater than 0.
-
-**📝 Note:** The `dpr` option affects gravities offsets, watermark offsets, and paddings to make the resulting image structures with and without the `dpr` option applied match.
-
-Default: `1`
-
-### Enlarge
-
-```
-enlarge:%enlarge
-el:%enlarge
-```
-
-When set to `1`, `t` or `true`, imgproxy will enlarge the image if it is smaller than the given size.
-
-Default: `false`
-
-### Extend
-
-```
-extend:%extend:%gravity
-ex:%extend:%gravity
-```
-
-* When `extend` is set to `1`, `t` or `true`, imgproxy will extend the image if it is smaller than the given size.
-* `gravity` _(optional)_ accepts the same values as the [gravity](#gravity) option, except `sm`. When `gravity` is not set, imgproxy will use `ce` gravity without offsets.
-
-Default: `false:ce:0:0`
-
-### Extend aspect ratio
-
-```
-extend_aspect_ratio:%extend:%gravity
-extend_ar:%extend:%gravity
-exar:%extend:%gravity
-```
-
-* When `extend` is set to `1`, `t` or `true`, imgproxy will extend the image to the requested aspect ratio.
-* `gravity` _(optional)_ accepts the same values as the [gravity](#gravity) option, except `sm`. When `gravity` is not set, imgproxy will use `ce` gravity without offsets.
-
-Default: `false:ce:0:0`
-
-### Gravity
-
-```
-gravity:%type:%x_offset:%y_offset
-g:%type:%x_offset:%y_offset
-```
-
-When imgproxy needs to cut some parts of the image, it is guided by the gravity option.
-
-* `type` - specifies the gravity type. Available values:
- * `no`: north (top edge)
- * `so`: south (bottom edge)
- * `ea`: east (right edge)
- * `we`: west (left edge)
- * `noea`: north-east (top-right corner)
- * `nowe`: north-west (top-left corner)
- * `soea`: south-east (bottom-right corner)
- * `sowe`: south-west (bottom-left corner)
- * `ce`: center
-* `x_offset`, `y_offset` - (optional) specifies the gravity offset along the X and Y axes.
-
-Default: `ce:0:0`
-
-**Special gravities**:
-
-* `gravity:sm`: smart gravity. `libvips` detects the most "interesting" section of the image and considers it as the center of the resulting image. Offsets are not applicable here.
-* `gravity:obj:%class_name1:%class_name2:...:%class_nameN`: ![pro](./assets/pro.svg) object-oriented gravity. imgproxy [detects objects](object_detection.md) of provided classes on the image and calculates the resulting image center using their positions. If class names are omited, imgproxy will use all the detected objects.
-* `gravity:fp:%x:%y`: the gravity focus point . `x` and `y` are floating point numbers between 0 and 1 that define the coordinates of the center of the resulting image. Treat 0 and 1 as right/left for `x` and top/bottom for `y`.
-
-### Crop
-
-```
-crop:%width:%height:%gravity
-c:%width:%height:%gravity
-```
-
-Defines an area of the image to be processed (crop before resize).
-
-* `width` and `height` define the size of the area:
- * When `width` or `height` is greater than or equal to `1`, imgproxy treats it as an absolute value.
- * When `width` or `height` is less than `1`, imgproxy treats it as a relative value.
- * When `width` or `height` is set to `0`, imgproxy will use the full width/height of the source image.
-* `gravity` _(optional)_ accepts the same values as the [gravity](#gravity) option. When `gravity` is not set, imgproxy will use the value of the [gravity](#gravity) option.
-
-### Trim
-
-```
-trim:%threshold:%color:%equal_hor:%equal_ver
-t:%threshold:%color:%equal_hor:%equal_ver
-```
-
-Removes surrounding background.
-
-* `threshold` - color similarity tolerance.
-* `color` - _(optional)_ a hex-coded value of the color that needs to be cut off.
-* `equal_hor` - _(optional)_ set to `1`, `t` or `true`, imgproxy will cut only equal parts from left and right sides. That means that if 10px of background can be cut off from the left and 5px from the right, then 5px will be cut off from both sides. For example, this can be useful if objects on your images are centered but have non-symmetrical shadow.
-* `equal_ver` - _(optional)_ acts like `equal_hor` but for top/bottom sides.
-
-**⚠️ Warning:** Trimming requires an image to be fully loaded into memory. This disables scale-on-load and significantly increases memory usage and processing time. Use it carefully with large images.
-
-**📝 Note:** If you know background color of your images then setting it explicitly via `color` will also save some resources because imgproxy won't need to automatically detect it.
-
-**📝 Note:** Use a `color` value of `FF00FF` for trimming transparent backgrounds as imgproxy uses magenta as a transparency key.
-
-**📝 Note:** The trimming of animated images is not supported.
-
-### Padding
-
-```
-padding:%top:%right:%bottom:%left
-pd:%top:%right:%bottom:%left
-```
-
-Defines padding size using CSS-style syntax. All arguments are optional but at least one dimension must be set. Padded space is filled according to the [background](#background) option.
-
-* `top` - top padding (and for all other sides if they haven't been explicitly st)
-* `right` - right padding (and left if it hasn't been explicitly set)
-* `bottom` - bottom padding
-* `left` - left padding
-
-**📝 Note:** Padding is applied after all image transformations (except watermarking) and enlarges the generated image. This means that if your resize dimensions were 100x200px and you applied the `padding:10` option, then you will end up with an image with dimensions of 120x220px.
-
-**📝 Note:** Padding follows the [dpr](#dpr) option so it will also be scaled if you've set it.
-
-### Auto rotate
-
-```
-auto_rotate:%auto_rotate
-ar:%auto_rotate
-```
-
-When set to `1`, `t` or `true`, imgproxy will automatically rotate images based on the EXIF Orientation parameter (if available in the image meta data). The orientation tag will be removed from the image in all cases. Normally this is controlled by the [IMGPROXY_AUTO_ROTATE](configuration.md#miscellaneous) configuration but this procesing option allows the configuration to be set for each request.
-
-### Rotate
-
-```
-rotate:%angle
-rot:%angle
-```
-
-Rotates the image on the specified angle. The orientation from the image metadata is applied before the rotation unless autorotation is disabled.
-
-**📝 Note:** Only 0, 90, 180, 270, etc., degree angles are supported.
-
-Default: 0
-
-### Background
-
-```
-background:%R:%G:%B
-bg:%R:%G:%B
-
-background:%hex_color
-bg:%hex_color
-```
-
-When set, imgproxy will fill the resulting image background with the specified color. `R`, `G`, and `B` are the red, green and blue channel values of the background color (0-255). `hex_color` is a hex-coded value of the color. Useful when you convert an image with alpha-channel to JPEG.
-
-With no arguments provided, disables any background manipulations.
-
-Default: disabled
-
-### Background alpha![pro](./assets/pro.svg) :id=background-alpha
-
-```
-background_alpha:%alpha
-bga:%alpha
-```
-
-Adds an alpha channel to `background`. The value of `alpha` is a positive floating point number between `0` and `1`.
-
-Default: 1
-
-### Adjust![pro](./assets/pro.svg) :id=adjust
-
-```
-adjust:%brightness:%contrast:%saturation
-a:%brightness:%contrast:%saturation
-```
-
-This is a meta-option that defines the [brightness](#brightness), [contrast](#contrast), and [saturation](#saturation). All arguments are optional and can be omitted to use their default values.
-
-### Brightness![pro](./assets/pro.svg) :id=brightness
-
-```
-brightness:%brightness
-br:%brightness
-```
-
-When set, imgproxy will adjust brightness of the resulting image. `brightness` is an integer number ranging from `-255` to `255`.
-
-Default: 0
-
-### Contrast![pro](./assets/pro.svg) :id=contrast
-
-```
-contrast:%contrast
-co:%contrast
-```
-
-When set, imgproxy will adjust the contrast of the resulting image. `contrast` is a positive floating point number, where a value of `1` leaves the contrast unchanged.
-
-Default: 1
-
-### Saturation![pro](./assets/pro.svg) :id=saturation
-
-```
-saturation:%saturation
-sa:%saturation
-```
-
-When set, imgproxy will adjust saturation of the resulting image. `saturation` is a positive floating-point number, where a value of `1` leaves the saturation unchanged.
-
-Default: 1
-
-### Blur
-
-```
-blur:%sigma
-bl:%sigma
-```
-
-When set, imgproxy will apply a gaussian blur filter to the resulting image. The value of `sigma` defines the size of the mask imgproxy will use.
-
-Default: disabled
-
-### Sharpen
-
-```
-sharpen:%sigma
-sh:%sigma
-```
-
-When set, imgproxy will apply the sharpen filter to the resulting image. The value of `sigma` defines the size of the mask imgproxy will use.
-
-As an approximate guideline, use 0.5 sigma for 4 pixels/mm (display resolution), 1.0 for 12 pixels/mm and 1.5 for 16 pixels/mm (300 dpi == 12 pixels/mm).
-
-Default: disabled
-
-### Pixelate
-
-```
-pixelate:%size
-pix:%size
-```
-
-When set, imgproxy will apply the pixelate filter to the resulting image. The value of `size` defines individual pixel size.
-
-Default: disabled
-
-### Unsharp masking![pro](./assets/pro.svg) :id=unsharp-masking
-
-```
-unsharp_masking:%mode:%weight:%divider
-ush:%mode:%weight:%divider
-```
-
-Allows redefining unsharp masking options. All arguments have the same meaning as [Unsharp masking](configuration.md#unsharp-masking) configs. All arguments are optional and can be omitted.
-
-### Blur detections![pro](./assets/pro.svg) :id=blur-detections
-
-```
-blur_detections:%sigma:%class_name1:%class_name2:...:%class_nameN
-bd:%sigma:%class_name1:%class_name2:...:%class_nameN
-```
-
-imgproxy [detects objects](object_detection.md) of the provided classes and blurs them. If class names are omitted, imgproxy blurs all the detected objects.
-
-The value of `sigma` defines the size of the mask imgproxy will use.
-
-### Draw detections![pro](./assets/pro.svg) :id=draw-detections
-
-```
-draw_detections:%draw:%class_name1:%class_name2:...:%class_nameN
-dd:%draw:%class_name1:%class_name2:...:%class_nameN
-```
-
-When `draw` is set to `1`, `t` or `true`, imgproxy [detects objects](object_detection.md) of the provided classes and draws their bounding boxes. If class names are omitted, imgproxy draws the bounding boxes of all the detected objects.
-
-### Gradient![pro](./assets/pro.svg) :id=gradient
-
-```
-gradient:%opacity:%color:%direction:%start%stop
-gr:%opacity:%color:%direction:%start%stop
-```
-
-Places a gradient on the processed image. The placed gradient transitions from transparency to the specified color.
-
-* `opacity`: specifies gradient opacity. When set to `0`, gradient is not applied.
-* `color`: _(optional)_ a hex-coded value of the gradient color. Default: `000` (black).
-* `direction`: _(optional)_ specifies the direction of the gradient. Available values:
- * `down`: _(default)_ the top side of the gradient is transparrent, the bottom side is opaque
- * `up`: the bottom side of the gradient is transparrent, the top side is opaque
- * `right`: the left side of the gradient is transparrent, the right side is opaque
- * `left`: the right side of the gradient is transparrent, the left side is opaque
-* `start`, `stop`: floating point numbers that define relative positions of where the gradient starts and where it ends. Default values are `0.0` and `1.0` respectively.
-
-### Watermark
-
-```
-watermark:%opacity:%position:%x_offset:%y_offset:%scale
-wm:%opacity:%position:%x_offset:%y_offset:%scale
-```
-
-Places a watermark on the processed image.
-
-* `opacity`: watermark opacity modifier. Final opacity is calculated like `base_opacity * opacity`.
-* `position`: (optional) specifies the position of the watermark. Available values:
- * `ce`: (default) center
- * `no`: north (top edge)
- * `so`: south (bottom edge)
- * `ea`: east (right edge)
- * `we`: west (left edge)
- * `noea`: north-east (top-right corner)
- * `nowe`: north-west (top-left corner)
- * `soea`: south-east (bottom-right corner)
- * `sowe`: south-west (bottom-left corner)
- * `re`: repeat and tile the watermark to fill the entire image
-* `x_offset`, `y_offset` - (optional) specify watermark offset by X and Y axes. When using `re` position, these values define the spacing between the tiles.
-* `scale`: (optional) a floating-point number that defines the watermark size relative to the resultant image size. When set to `0` or when omitted, the watermark size won't be changed.
-
-Default: disabled
-
-### Watermark URL![pro](./assets/pro.svg) :id=watermark-url
-
-```
-watermark_url:%url
-wmu:%url
-```
-
-When set, imgproxy will use the image from the specified URL as a watermark. `url` is the URL-safe Base64-encoded URL of the custom watermark.
-
-Default: blank
-
-### Watermark text![pro](./assets/pro.svg) :id=watermark-text
-
-```
-watermark_text:%text
-wmt:%text
-```
-
-When set, imgproxy will generate an image from the provided text and use it as a watermark. `text` is the URL-safe Base64-encoded text of the custom watermark.
-
-By default, the text color is black and the font is `sans 16`. You can use [Pango markup](https://docs.gtk.org/Pango/pango_markup.html) in the `text` value to change the style.
-
-If you want to use a custom font, you need to put it in `/usr/share/fonts` inside a container.
-
-Default: blank
-
-### Watermark size![pro](./assets/pro.svg) :id=watermark-size
-
-```
-watermark_size:%width:%height
-wms:%width:%height
-```
-
-Defines the desired width and height of the watermark. imgproxy always uses `fit` resizing type when resizing watermarks and enlarges them when needed.
-
-When `%width` is set to `0`, imgproxy will calculate the width using the defined height and watermark's aspect ratio.
-
-When `%height` is set to `0`, imgproxy will calculate the height using the defined width and watermark's aspect ratio.
-
-**📝 Note:** This processing option takes effect only when the `scale` argument of the `watermark` option is set to zero.
-
-Default: `0:0`
-
-### Watermark shadow![pro](./assets/pro.svg) :id=watermark-shadow
-
-```
-watermark_shadow:%sigma
-wmsh:%sigma
-```
-When set, imgproxy will add a shadow to the watermark. The value of `sigma` defines the size of the mask imgproxy will use to blur the shadow.
-
-Default: disabled
-
-### Style![pro](./assets/pro.svg) :id=style
-
-```
-style:%style
-st:%style
-```
-
-When set, imgproxy will prepend a `